lower code smells
[appc.git] / appc-dispatcher / appc-workflow-management / appc-workflow-management-core / src / main / java / org / onap / appc / workflow / impl / WorkFlowManagerImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Copyright (C) 2017 Amdocs
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.appc.workflow.impl;
24
25 import org.apache.commons.lang.ObjectUtils;
26 import org.onap.appc.common.constant.Constants;
27 import org.onap.appc.configuration.Configuration;
28 import org.onap.appc.configuration.ConfigurationFactory;
29 import org.onap.appc.domainmodel.lcm.RequestContext;
30 import org.onap.appc.domainmodel.lcm.ResponseContext;
31 import org.onap.appc.util.ObjectMapper;
32 import org.onap.appc.workflow.WorkFlowManager;
33 import org.onap.appc.workflow.objects.WorkflowExistsOutput;
34 import org.onap.appc.workflow.objects.WorkflowRequest;
35 import org.onap.appc.workflow.objects.WorkflowResponse;
36 import com.att.eelf.configuration.EELFLogger;
37 import com.att.eelf.configuration.EELFManager;
38 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
39 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
40 import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService;
41
42 import org.osgi.framework.Bundle;
43 import org.osgi.framework.BundleContext;
44 import org.osgi.framework.FrameworkUtil;
45 import org.osgi.framework.ServiceReference;
46 import java.util.ArrayList;
47 import org.onap.ccsdk.sli.core.dblib.DbLibService;
48 import org.onap.ccsdk.sli.core.sli.SvcLogicGraph;
49 import javax.sql.rowset.CachedRowSet;
50 import java.sql.SQLException;
51 import java.io.ObjectInputStream;
52 import java.io.IOException;
53 import org.slf4j.MDC;
54
55
56
57 import java.text.SimpleDateFormat;
58 import java.util.Arrays;
59 import java.util.Enumeration;
60 import java.util.Map;
61 import java.util.Properties;
62
63
64 public class WorkFlowManagerImpl implements WorkFlowManager{
65     private static final String DRIVER_ERROR = "DriverLoadError";
66     private SvcLogicService svcLogic = null;
67     private final EELFLogger logger = EELFManager.getInstance().getLogger(WorkFlowManagerImpl.class);
68     private final Configuration configuration = ConfigurationFactory.getConfiguration();
69
70     private  WorkflowResolver workflowResolver = new WorkflowResolver(
71             configuration.getIntegerProperty("org.onap.appc.workflow.resolver.refresh_interval", 300)
72     );
73
74     public void setWorkflowResolver(WorkflowResolver workflowResolver){
75         this.workflowResolver = workflowResolver;
76     }
77
78     public void setSvcLogicServiceRef(SvcLogicService svcLogic) {
79         this.svcLogic = svcLogic;
80     }
81
82     /**
83      * Execute workflow and return response.
84      * This method execute workflow with following steps.
85      * Retrieve workflow(DG) details - module, version and mode from database
86      * based on command and vnf Type from incoming request.
87      * Execute workflow (DG) using SVC Logic Service reference
88      * Return response of workflow (DG) to caller.
89      *
90      * @param workflowRequest workflow execution request which contains vnfType,
91      * command, requestId, targetId, payload and (optional) confID;
92      * @return Workflow Response which contains execution status and payload from DG if any
93      */
94
95     @Override
96     public WorkflowResponse executeWorkflow(WorkflowRequest workflowRequest) {
97         logger.trace("Entering to executeWorkflow with WorkflowRequest = "
98                 + ObjectUtils.toString(workflowRequest.toString()));
99         WorkflowResponse workflowResponse = new WorkflowResponse();
100         workflowResponse.setResponseContext(workflowRequest.getResponseContext());
101
102         try {
103             WorkflowKey workflowKey =
104                     workflowResolver.resolve(workflowRequest.getRequestContext().getAction().name(),
105                         workflowRequest.getVnfContext().getType(), null,
106                         workflowRequest.getRequestContext().getCommonHeader().getApiVer());
107
108             Properties workflowParams = new Properties();
109             String actionProperty;
110             String requestIdProperty;
111             String vfIdProperty;
112             if (!workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")) {
113                 /*
114                 The following method call (populateDGContext) populates DG context with the
115                 request parameters to maintain backward compatibility with old DGs,
116                 we are not altering the old way of passing (org.onap.appc.vnfId and so on..)
117                 This is still a temporary solution, the end solution should be agreed with
118                 all stakeholders and implemented.
119              */
120                 populateDGContext(workflowParams, workflowRequest);
121             } else {
122                 actionProperty =
123                         configuration.getProperty("org.onap.appc.workflow.action", String.valueOf(Constants.ACTION));
124                 requestIdProperty =
125                         configuration.getProperty("org.onap.appc.workflow.request.id",
126                         String.valueOf(Constants.REQUEST_ID));
127                 vfIdProperty =
128                         configuration.getProperty("org.onap.appc.workflow.vfid", String.valueOf(Constants.VF_ID));
129                 String vfTypeProperty =
130                         configuration.getProperty("org.onap.appc.workflow.vftype", String.valueOf(Constants.VF_TYPE));
131                 String apiVerProperty =
132                         configuration.getProperty("org.onap.appc.workflow.apiVersion",
133                         String.valueOf(Constants.API_VERSION));
134                 String originatorIdProperty =
135                         configuration.getProperty("org.onap.appc.workflow.originatorId", Constants.ORIGINATOR_ID);
136                 String subRequestId =
137                         configuration.getProperty("org.onap.appc.workflow.subRequestId", Constants.SUB_REQUEST_ID);
138
139                 workflowParams.put(actionProperty, workflowRequest.getRequestContext().getAction().name());
140                 workflowParams.put(requestIdProperty,
141                         workflowRequest.getRequestContext().getCommonHeader().getRequestId());
142                 workflowParams.put(vfIdProperty, workflowRequest.getVnfContext().getId());
143                 workflowParams.put(vfTypeProperty, workflowRequest.getVnfContext().getType());
144                 workflowParams.put(apiVerProperty, workflowRequest.getRequestContext().getCommonHeader().getApiVer());
145                 workflowParams.put(originatorIdProperty,
146                         workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
147                 workflowParams.put(subRequestId,
148                         workflowRequest.getRequestContext().getCommonHeader().getSubRequestId());
149
150                 Object payloadJson = workflowRequest.getRequestContext().getPayload();
151                 if (payloadJson != null) {
152                     try {
153                         Map<String, String> payloadProperties = ObjectMapper.map(payloadJson);
154                         workflowParams.putAll(payloadProperties);
155
156                         logger.debug("DG properties: " + workflowParams);
157                     } catch (Exception e) {
158                         logger.error("Error parsing payload json string", e);
159                         Properties workflowPrp = new Properties();
160                         workflowPrp.setProperty("error-message", "Error parsing payload json string");
161                         fillStatus(501, "Error parsing payload json string: " + e.getMessage(),
162                                 workflowRequest.getResponseContext());
163                         logger.trace("Exiting from executeWorkflow with (workflowResponse = "
164                                 + ObjectUtils.toString(workflowResponse) + ")");
165                         return workflowResponse;
166                     }
167                 }
168                 logger.debug("DG parameters " + actionProperty + ":"
169                         + workflowRequest.getRequestContext().getAction().name() + ", "
170                         + requestIdProperty + ":"
171                         + workflowRequest.getRequestContext().getCommonHeader().getRequestId() + ", "
172                         + vfIdProperty + ":" + workflowRequest.getVnfContext().getId());
173
174                 logger.debug("Starting DG Execution for request "
175                         + workflowRequest.getRequestContext().getCommonHeader().getRequestId());
176             }
177             if (workflowRequest.getRequestContext().getCommonHeader().getApiVer().startsWith("1.")) {
178                 workflowParams.put("isBwcMode", "true");
179             } else {
180                 workflowParams.put("isBwcMode", "false");
181             }
182
183             SVCLogicServiceExecute(workflowKey, workflowRequest.getRequestContext(), workflowParams,
184                     workflowResponse);
185             logger.trace("Completed DG Execution for Request id: "
186                     + workflowRequest.getRequestContext().getCommonHeader().getRequestId() + " with response code: "
187                     + workflowResponse.getResponseContext().getStatus().getCode());
188         } catch (Exception e) {
189             logger.error("Error Executing DG " + e.getMessage(), e);
190             fillStatus(501, "Error Executing DG " + e.getMessage(), workflowRequest.getResponseContext());
191         }
192         logger.trace("Exiting from executeWorkflow with (workflowResponse = "
193                 + ObjectUtils.toString(workflowResponse.getResponseContext().getStatus().getMessage()) + ")");
194         return workflowResponse;
195     }
196
197     private void populateDGContext(Properties workflowParams, WorkflowRequest workflowRequest) {
198         workflowParams.put("input.common-header.timestamp",
199                 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
200                     .format(workflowRequest.getRequestContext().getCommonHeader().getTimeStamp()));
201         workflowParams.put("input.common-header.api-ver",
202                 workflowRequest.getRequestContext().getCommonHeader().getApiVer());
203         workflowParams.put("input.common-header.request-id",
204                 workflowRequest.getRequestContext().getCommonHeader().getRequestId());
205         workflowParams.put("input.common-header.originator-id",
206                 workflowRequest.getRequestContext().getCommonHeader().getOriginatorId());
207         workflowParams.put("input.common-header.sub-request-id",
208                 workflowRequest.getRequestContext().getCommonHeader().getSubRequestId() != null ?
209                 workflowRequest.getRequestContext().getCommonHeader().getSubRequestId() : "");
210         workflowParams.put("input.action", workflowRequest.getRequestContext().getAction().toString());
211         workflowParams.put("input.payload",
212                 null != workflowRequest.getRequestContext().getPayload() ?
213                 workflowRequest.getRequestContext().getPayload() : "");
214         workflowParams.put("input.action-identifiers.vnf-id", workflowRequest.getVnfContext().getId());
215         workflowParams.put("input.action-identifiers.vnfc-name",
216                 workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName() != null ?
217                 workflowRequest.getRequestContext().getActionIdentifiers().getVnfcName() : "");
218         workflowParams.put("input.action-identifiers.service-instance-id",
219                 workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId() != null ?
220                 workflowRequest.getRequestContext().getActionIdentifiers().getServiceInstanceId() : "");
221         workflowParams.put("input.action-identifiers.vserver-id",
222                 workflowRequest.getRequestContext().getActionIdentifiers().getVserverId() != null ?
223                 workflowRequest.getRequestContext().getActionIdentifiers().getVserverId() : "");
224         workflowParams.put("input.action-identifiers.vf-module-id",
225                 workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId() != null ?
226                 workflowRequest.getRequestContext().getActionIdentifiers().getVfModuleId() : "");
227         final Map<String, String> additionalContext;
228         if ((additionalContext = workflowRequest.getRequestContext().getAdditionalContext()) != null) {
229             for (Map.Entry<String, String> entry : additionalContext.entrySet()) {
230                 workflowParams.put("input." + entry.getKey(), null != entry.getValue() ? entry.getValue() : "");
231     }
232         }
233     }
234
235     /**
236      * Check if workflow (DG) exists in database
237      *
238      * @param workflowQueryParams workflow request with command and vnf Type
239      * @return True if workflow exists else False.
240      */
241     @Override
242     public WorkflowExistsOutput workflowExists(WorkflowRequest workflowQueryParams) {
243         WorkflowExistsOutput workflowExistsOutput = new WorkflowExistsOutput(false, false);
244         logger.trace("Entering to workflowExists with WorkflowRequest = "
245                 + ObjectUtils.toString(workflowQueryParams.toString()));
246
247         try {
248             WorkflowKey workflowKey = workflowResolver.resolve(
249                     workflowQueryParams.getRequestContext().getAction().name(),
250                     workflowQueryParams.getVnfContext().getType(),
251                     workflowQueryParams.getVnfContext().getVersion(),
252                     workflowQueryParams.getRequestContext().getCommonHeader().getApiVer());
253             if (workflowKey != null) {
254                 workflowExistsOutput.setMappingExist(true);
255                 workflowExistsOutput.setWorkflowModule(workflowKey.module());
256                 workflowExistsOutput.setWorkflowName(workflowKey.name());
257                 workflowExistsOutput.setWorkflowVersion(workflowKey.version());
258                 if (isDGExists(workflowKey)) {
259                     workflowExistsOutput.setDgExist(true);
260                 } else {
261                     logger.warn(
262                             String.format("SLI doesn't have DG for resolved mapping entry:  "
263                                 + "DG module - '%s', DG name - '%s', DG version - '%s'",
264                                 workflowKey.module(), workflowKey.name(), workflowKey.version()));
265                 }
266             } else {
267                 logger.warn(
268                         String.format("Unable to resolve recipe matching action '%s', VNF type '%s'"
269                             + " and VNF version '%s'",
270                             workflowQueryParams.getRequestContext().getAction().name(),
271                             workflowQueryParams.getVnfContext().getType(), null));
272             }
273         } catch (RuntimeException e) {
274             logger.error("Error querying workflow from database" + e.getMessage());
275             throw e;
276         } catch (SvcLogicException e) {
277             logger.error("Error querying workflow from database" + e.getMessage());
278             throw new RuntimeException(e);
279         }
280         logger.trace("Exiting workflowExists");
281         return workflowExistsOutput;
282     }
283
284
285     private boolean isDGExists(WorkflowKey workflowKey) throws SvcLogicException {
286         return svcLogic.hasGraph(workflowKey.module(), workflowKey.name(), workflowKey.version(), "sync");
287     }
288
289
290     private void restartDbLibProvider() {
291
292         Bundle bundle = FrameworkUtil.getBundle(org.onap.ccsdk.sli.core.dblib.DbLibService.class);
293         try {
294             bundle.stop();
295             bundle.start();
296             //Thread.sleep(5000);
297         } catch (org.osgi.framework.BundleException be) {
298             logger.error("Error restarting db lib" + be.toString());
299         } /*catch (InterruptedException e) {
300         }*/
301     }
302
303     private SvcLogicGraph fetchGraph(String module, String rpc, String version, String mode, StringBuilder sbError)
304     {
305         DbLibService dbLibSvc = null;
306
307         ServiceReference sref = null;
308         BundleContext bctx = null;
309
310         Bundle bundle = FrameworkUtil.getBundle(WorkFlowManagerImpl.class);
311
312         if (bundle != null) {
313             bctx = bundle.getBundleContext();
314
315             if (bctx != null) {
316                 sref = bctx.getServiceReference("org.onap.ccsdk.sli.core.dblib.DbLibService");
317             }
318
319             if (sref == null) {
320                 logger.warn("Could not find service reference for DBLIB service");
321             } else {
322                 dbLibSvc = (DbLibService) bctx.getService(sref);
323                 if (dbLibSvc == null) {
324                     logger.warn("Could not find service reference for DBLIB service");
325                 }
326             }
327         }
328         if (dbLibSvc == null)
329             return null;
330         else
331             logger.info("Retrieving graph(new)");
332
333         SvcLogicGraph retval = null;
334         CachedRowSet results = null;
335
336         String fetchVersionGraphSql = "SELECT graph FROM SVC_LOGIC"
337                 + " WHERE module = ? AND rpc = ? AND mode = ? AND version = ?";
338
339         String fetchActiveGraphSql = "SELECT graph FROM SVC_LOGIC"
340                 + " WHERE module = ? AND rpc = ? AND mode = ? AND active = 'Y'";
341
342
343         String fetchGraphStmt;
344
345         ArrayList<String> params = new ArrayList<>();
346         params.add(module);
347         params.add(rpc);
348         params.add(mode);
349
350         if (version == null) {
351             fetchGraphStmt = fetchActiveGraphSql;
352         } else {
353             params.add(version);
354             fetchGraphStmt = fetchVersionGraphSql;
355         }
356
357         StringBuilder sqlBuilder = new StringBuilder(fetchGraphStmt);
358         try {
359             results = dbLibSvc.getData(sqlBuilder.toString(), new ArrayList(params), "sdnctl");
360
361             if (results.next()) {
362
363                 ObjectInputStream gStream = new ObjectInputStream(results.getBinaryStream("graph"));
364
365                 Object graphObj = gStream.readObject();
366                 gStream.close();
367
368                 if (graphObj instanceof SvcLogicGraph) {
369                     retval = (SvcLogicGraph) graphObj;
370                 } else {
371                     logger.error("invalid type for graph " + graphObj.getClass().getName());
372                     return null;
373                 }
374
375             } else {
376                 return null;
377             }
378         } catch (SQLException e) {
379             logger.error("query " + sqlBuilder + " :: " + e.getMessage() + " Will retry");
380             //sbError.append(DRIVER_ERROR);
381             return null;
382         } catch (IOException e) {
383             logger.error("IOException "  + " :: " + e.getMessage() + " Will retry");
384             //sbError.append(DRIVER_ERROR);
385             return null;
386         } catch (Exception e) {
387             logger.error("Exception "  + " :: " + e.getMessage() + " Will retry");
388             sbError.append(DRIVER_ERROR);
389             return null;
390         }
391
392
393         return retval;
394     }
395
396     protected Properties workflowExecute(String module, String rpc, String version, String mode, Properties props)
397             throws SvcLogicException {
398         logger.info("Fetching service logic from data store");
399         //logger.info("Trial Restart ");
400         //restartDbLibProvider();
401         //logger.info("Trial Restart End");
402
403         StringBuilder sbError = new StringBuilder();
404         SvcLogicGraph graph = fetchGraph(module, rpc, version, mode, sbError);
405         if (sbError.toString().equals(DRIVER_ERROR))
406         {
407             restartDbLibProvider();
408             sbError = new StringBuilder();
409             graph = fetchGraph(module, rpc, version, mode, sbError);
410         }
411         if (graph == null) {
412             Properties retProps = new Properties();
413             retProps.setProperty("error-code", "401");
414             retProps.setProperty("error-message",
415                     "No service logic found for [" + module + "," + rpc + "," + version + "," + mode + "]");
416             return (retProps);
417         }
418
419         SvcLogicContext ctx = new SvcLogicContext(props);
420         ctx.setAttribute("currentGraph", graph.toString());
421         ctx.setAttribute("X-ONAP-RequestID", MDC.get("X-ONAP-RequestID"));
422         svcLogic.execute(graph, ctx);
423         return (ctx.toProperties());
424     }
425
426     private void SVCLogicServiceExecute(WorkflowKey workflowKey, RequestContext requestContext,
427             Properties workflowParams, WorkflowResponse workflowResponse) {
428         logger.trace("Entering SVCLogicServiceExecute");
429
430         Properties respProps = null;
431
432         try {
433             respProps = workflowExecute(workflowKey.module(), workflowKey.name(), workflowKey.version(),
434                     "sync", workflowParams);
435         } catch (Exception e) {
436             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), "failure",
437                     "Unexpected SLI Adapter failure", 200);
438
439             String stk = "";
440             if (logger.isDebugEnabled()) {
441                 stk = Arrays.toString(e.getStackTrace());
442                 logger.debug("Error while executing DG " + e.getMessage() + stk);
443             }
444             logger.error("Error in DG", e.getMessage() + stk, e);
445         }
446
447         if (respProps != null) {
448             if (!requestContext.getCommonHeader().getApiVer().startsWith("1.")) {
449                 fillResponseContextByOutputFieldsFromDgContext(workflowResponse.getResponseContext(), respProps);
450             }
451
452             final String commonStatus = respProps.getProperty(Constants.DG_ATTRIBUTE_STATUS);
453             final String specificStatusMessage = respProps.getProperty(Constants.DG_OUTPUT_STATUS_MESSAGE);
454             String dgOutputStatusCode = respProps.getProperty(Constants.DG_OUTPUT_STATUS_CODE);
455             int specificStatusCode = 0;
456             if (dgOutputStatusCode != null) {
457                 specificStatusCode = Integer.parseInt(dgOutputStatusCode);
458             }
459
460             setWorkFlowResponseStatus(workflowResponse.getResponseContext(), commonStatus, specificStatusMessage,
461                     specificStatusCode);
462
463             logger.debug("DG Execution Status: " + commonStatus);
464         }
465
466         logger.trace("Exiting from SVCLogicServiceExecute");
467     }
468
469     /**
470      * Filling response context by output.* fields from DG context. Works only for 2.* API version
471      *
472      * @param responseContext response context which you need to fill
473      * @param respProps DG context in a properties format
474      */
475     private void fillResponseContextByOutputFieldsFromDgContext(ResponseContext responseContext,
476             Properties respProps) {
477
478         Enumeration<?> e = respProps.propertyNames();
479         while (e.hasMoreElements()) {
480             String key = (String) e.nextElement();
481             if (key.startsWith("output.")) {
482                 if (!key.startsWith("output.common-header.") && !key.startsWith("output.status.")) {
483
484                     if (key.equalsIgnoreCase("output.payload")) {
485                         responseContext.setPayload(respProps.getProperty(key));
486                     } else {
487                         responseContext.addKeyValueToAdditionalContext(key, respProps.getProperty(key));
488                     }
489                 }
490             }
491         }
492     }
493
494     /**
495      * Filling responseContext status code and message according to response messages and codes from DG.
496      *
497      * @param responseContext response cotext
498      * @param commonStatus common status message from DG ("success" or "failure")
499      * @param specificStatusMessage specific status message from specific DG node
500      * @param specificStatusCode specific status code from specific DG node
501      */
502     private void setWorkFlowResponseStatus(ResponseContext responseContext, String commonStatus,
503             String specificStatusMessage, int specificStatusCode) {
504         if (null == specificStatusMessage) { specificStatusMessage = ""; }
505         if (commonStatus.equalsIgnoreCase(Constants.DG_STATUS_SUCCESS)) {
506             if (specificStatusCode != 0) {
507                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
508             } else {
509                 fillStatus(400, commonStatus, responseContext);
510             }
511         } else {
512             if (specificStatusCode != 0) {
513                 fillStatus(specificStatusCode, specificStatusMessage, responseContext);
514             } else {
515                 fillStatus(401, specificStatusMessage, responseContext);
516             }
517         }
518     }
519
520     /**
521      * filling responseContext by status code and status message
522      *
523      * @param code 3-digit status code
524      * @param message explanation of a status code
525      * @param responseContext response context which will be store status code and status message
526      */
527     private void fillStatus(int code, String message, ResponseContext responseContext) {
528         responseContext.getStatus().setCode(code);
529         responseContext.getStatus().setMessage(message);
530     }
531
532
533 }