Change code in appc dispatcher for new LCMs in R6
[appc.git] / appc-dispatcher / appc-dispatcher-common / transaction-recorder / src / main / java / org / onap / appc / transactionrecorder / impl / TransactionRecorderImpl.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  * Modifications Copyright (C) 2019 Ericsson
10  * =============================================================================
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  * ============LICENSE_END=========================================================
23  */
24
25 package org.onap.appc.transactionrecorder.impl;
26
27 import org.apache.commons.lang.StringUtils;
28 import org.onap.appc.domainmodel.lcm.Flags;
29 import org.onap.appc.domainmodel.lcm.RequestStatus;
30 import org.onap.appc.domainmodel.lcm.VNFOperation;
31 import org.onap.appc.exceptions.APPCException;
32 import org.onap.appc.transactionrecorder.TransactionRecorder;
33 import org.onap.appc.domainmodel.lcm.TransactionRecord;
34 import org.onap.appc.transactionrecorder.objects.TransactionConstants;
35 import org.onap.ccsdk.sli.core.dblib.DbLibService;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import java.sql.SQLException;
40 import javax.sql.rowset.CachedRowSet;
41 import java.text.ParseException;
42 import java.text.SimpleDateFormat;
43 import java.time.Instant;
44 import java.time.ZoneOffset;
45 import java.time.format.DateTimeFormatter;
46 import java.time.temporal.ChronoUnit;
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.Map;
50
51 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.TRANSACTION_ATTRIBUTES.*;
52 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.*;
53
54
55 public class TransactionRecorderImpl implements TransactionRecorder {
56
57     private final String SCHEMA = "sdnctl";
58
59     private String appcInstanceId;
60
61     private DbLibService dbLibService;
62
63     public void setDbLibService(DbLibService dbLibService) {
64         this.dbLibService = dbLibService;
65     }
66
67     private static final Logger logger = LoggerFactory.getLogger(TransactionRecorderImpl.class);
68
69     /**
70      * Stores transaction record to appc database by calling APPC Dao layer.
71      *
72      * @param record Transaction record data.
73      */
74     @Override
75     public void store(TransactionRecord record) throws APPCException {
76         logger.trace("Transaction data insertion into DB");
77         final String STORE_DATE_QUERY =
78                 TransactionConstants.INSERT_INTO + TransactionConstants.TRANSACTIONS
79                 + "(" + TRANSACTION_ID.getColumnName() + TransactionConstants.COMMA
80                 + ORIGIN_TIMESTAMP.getColumnName() + TransactionConstants.COMMA
81                 + REQUEST_ID.getColumnName() + TransactionConstants.COMMA
82                 + SUBREQUEST_ID.getColumnName() + TransactionConstants.COMMA
83                 + ORIGINATOR_ID.getColumnName() + TransactionConstants.COMMA
84                 + START_TIME.getColumnName() + TransactionConstants.COMMA
85                 + END_TIME.getColumnName() + TransactionConstants.COMMA
86                 + TARGET_ID.getColumnName() + TransactionConstants.COMMA
87                 + TARGET_TYPE.getColumnName() + TransactionConstants.COMMA
88                 + OPERATION.getColumnName() + TransactionConstants.COMMA
89                 + RESULT_CODE.getColumnName() + TransactionConstants.COMMA
90                 + DESCRIPTION.getColumnName() + TransactionConstants.COMMA
91                 + STATE.getColumnName() + TransactionConstants.COMMA
92                 + SERVICE_INSTANCE_ID + TransactionConstants.COMMA
93                 + VNFC_NAME + TransactionConstants.COMMA
94                 + VSERVER_ID + TransactionConstants.COMMA
95                 + VF_MODULE_ID + TransactionConstants.COMMA
96                 + MODE + ") "
97                 + "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
98         try {
99             dbLibService.writeData(STORE_DATE_QUERY, prepareArguments(record), SCHEMA);
100         } catch (SQLException e) {
101             logger.error("Error on storing record " + record.toString(), e);
102             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
103         }
104         logger.trace("Transaction Data Inserted Successfully into DB");
105     }
106
107     @Override
108     public void update(String key,
109             String requestId,
110             Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns) throws APPCException {
111         logger.debug("Inside update in TransactionRecorderImpl");
112
113         if (appcInstanceId != null && checkIfNullInstanceEntryExist(key, requestId)) {
114             updateNullInstanceEntry(key, requestId, updateColumns);
115         } else {
116             updateTransactionEntry(key, updateColumns);
117         }
118         logger.trace("End of Update Transaction table.");
119     }
120
121     private void updateTransactionEntry(String key, Map<TRANSACTION_ATTRIBUTES, String> updateColumns)
122         throws APPCException {
123         ArrayList<String> values = new ArrayList<>();
124         logger.debug("Inside updateTransactionEntry");
125
126         StringBuilder queryBuilder = new StringBuilder("UPDATE TRANSACTIONS SET ");
127         for (Map.Entry<TransactionConstants.TRANSACTION_ATTRIBUTES, String> entry : updateColumns.entrySet()) {
128             queryBuilder.append(entry.getKey().getColumnName() + " = ? ,");
129             values.add(entry.getValue());
130             logger.debug("Value for " + entry.getKey().getColumnName() + " in Transaction table: " + entry.getValue());
131         }
132         queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
133         queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ?");
134         values.add(appcInstanceId + "~" + key);
135
136         String query = queryBuilder.toString();
137         try {
138             if (logger.isDebugEnabled()) {
139                 logger.debug("Before updating Transaction table the Query is: " + query);
140                 for (String value : values) {
141                     logger.debug("Value for Transaction table: " + value);
142                 }
143             }
144             Boolean result = dbLibService.writeData(query, values, SCHEMA);
145             logger.debug("Transactions table data update, writeData() result: " + result);
146         } catch (SQLException e) {
147             logger.error("Error in updating records in updateTransactionEntry " + e);
148             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
149         }
150         logger.trace("Transaction data updated successfully");
151     }
152
153     private void updateNullInstanceEntry(String key,
154             String requestId,
155             Map<TRANSACTION_ATTRIBUTES, String> updateColumns) throws APPCException {
156         logger.debug("Inside updateNullInstanceEntry");
157
158         ArrayList<String> values = new ArrayList<>();
159         StringBuilder queryBuilder = new StringBuilder("UPDATE TRANSACTIONS SET ");
160         for (Map.Entry<TransactionConstants.TRANSACTION_ATTRIBUTES, String> entry : updateColumns.entrySet()) {
161             queryBuilder.append(entry.getKey().getColumnName() + " = ? ,");
162             values.add(entry.getValue());
163         }
164         // queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
165         queryBuilder.append(TRANSACTION_ID.getColumnName() + " = ?");
166         queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ? AND "
167                             + REQUEST_ID.getColumnName() + " = ? ");
168         values.add(appcInstanceId + "~" + key);
169         values.add(null + "~" + key);
170         values.add(requestId);
171         String query = queryBuilder.toString();
172         try {
173             if (logger.isDebugEnabled()) {
174                 logger.debug("Before updating Transaction table the Query is: " + query);
175                 for (String value : values) {
176                     logger.debug("Value for Transaction table: " + value);
177                 }
178             }
179             Boolean result = dbLibService.writeData(query, values, SCHEMA);
180             logger.debug("Transactions table data update, writeData() result: " + result);
181
182         } catch (SQLException e) {
183             logger.error("Error in updating records in updateNullInstanceEntry " + e);
184             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
185         }
186
187     }
188
189     private boolean checkIfNullInstanceEntryExist(String key, String requestId) throws APPCException {
190         logger.debug("Entered checkIfNullInstanceEntryExist");
191         String nullInstanceCheckQuery = new String(
192                 "SELECT COUNT(*) as ROWCOUNT  FROM "
193                 + TransactionConstants.TRANSACTIONS + WHERE
194                 + TRANSACTION_ID.getColumnName() + " = ? AND "
195                 + REQUEST_ID.getColumnName() + " = ? ");
196
197         ArrayList<String> nullInstanceCheckParams = new ArrayList<>();
198         nullInstanceCheckParams.add(null + "~" + key);
199         nullInstanceCheckParams.add(requestId);
200
201         try (CachedRowSet rowSet = dbLibService.getData(nullInstanceCheckQuery, nullInstanceCheckParams, SCHEMA)) {
202             int noRows = 0;
203             if (rowSet != null && rowSet.first()) {
204                 noRows = rowSet.getInt("ROWCOUNT");
205                 logger.info("No of Rows in Transactions Table with TRANSACTION_ID: "
206                             + null + "~" + key + " and REQUEST_ID " + requestId + " is: " + noRows);
207             }
208             if (noRows > 0)
209                 return true;
210         } catch (SQLException e) {
211             logger.error("Error in checkIfNullInstanceEntryExist in the transaction table", e);
212             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
213         }
214         return false;
215     }
216
217     @Override
218     public void markTransactionsAborted(String appcInstanceId) {
219         logger.trace("marking in progress transactions to aborted");
220         final String updateQuery =
221                 "UPDATE " + TransactionConstants.TRANSACTIONS
222                 + " SET " + STATE.getColumnName() + " = '" + RequestStatus.ABORTED.name() + "',"
223                 + END_TIME.getColumnName() + " = ? "
224                 + WHERE + TRANSACTION_ID.getColumnName() + " LIKE '" + appcInstanceId + "%'  AND "
225                 + STATE.getColumnName() + " in (?,?)";
226
227         if (logger.isDebugEnabled()) {
228             logger.debug("Update query " + updateQuery + " appc-instance-id " + appcInstanceId);
229         }
230
231         ArrayList<String> arguments = new ArrayList<>();
232         arguments.add(dateToStringConverterMillis(Instant.now()));
233         arguments.add(RequestStatus.ACCEPTED.name());
234         arguments.add(RequestStatus.RECEIVED.name());
235         try {
236             dbLibService.writeData(updateQuery, arguments, SCHEMA);
237         } catch (SQLException e) {
238             String message = "In progress transactions couldn't be marked aborted on server start up";
239             logger.error(message);
240             throw new RuntimeException(message, e);
241         }
242         logger.trace("In progress transactions marked aborted");
243     }
244
245     @Override
246     public List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException {
247
248         String IN_PROGRESS_REQUESTS_QUERY =
249                 "SELECT * FROM "
250                 + TransactionConstants.TRANSACTIONS + WHERE
251                 + TARGET_ID + " = ? AND "
252                 + STATE.getColumnName() + " IN (?,?) AND "
253                 + START_TIME.getColumnName() + " < ?";
254
255         ArrayList<String> inProgressQueryParams = new ArrayList<>();
256         Instant window = record.getStartTime().minus(interval, ChronoUnit.HOURS);
257         inProgressQueryParams.add(record.getTargetId());
258         inProgressQueryParams.add(RequestStatus.RECEIVED.name());
259         inProgressQueryParams.add(RequestStatus.ACCEPTED.name());
260         inProgressQueryParams.add(dateToStringConverterMillis(record.getStartTime()));
261         if (interval > 0) {
262             IN_PROGRESS_REQUESTS_QUERY += " AND " + START_TIME.getColumnName() + " > ? ";
263             inProgressQueryParams.add(dateToStringConverterMillis(window));
264         }
265
266         try (CachedRowSet rowSet =
267                 dbLibService.getData(IN_PROGRESS_REQUESTS_QUERY, inProgressQueryParams, SCHEMA)) {
268             List<TransactionRecord> inProgressRecords = new ArrayList<>();
269             TransactionRecord transaction;
270             int count = 0;
271             if (rowSet != null) {
272                 for (; rowSet.next(); ++count) {
273                     transaction = new TransactionRecord();
274                     transaction.setTransactionId(rowSet.getString(TRANSACTION_ID.getColumnName()));
275                     transaction.setRequestId(rowSet.getString(REQUEST_ID.getColumnName()));
276                     transaction.setSubRequestId(rowSet.getString(SUBREQUEST_ID.getColumnName()));
277                     transaction.setOriginatorId(rowSet.getString(ORIGINATOR_ID.getColumnName()));
278                     transaction.setStartTime(stringToDateConverterMillis(rowSet.getString(START_TIME.getColumnName())));
279                     transaction.setTargetId(rowSet.getString(TARGET_ID.getColumnName()));
280                     transaction.setTargetType(rowSet.getString(TARGET_TYPE.getColumnName()));
281                     transaction.setOperation(VNFOperation.valueOf(rowSet.getString(OPERATION.getColumnName())));
282                     transaction.setRequestState(RequestStatus.valueOf(rowSet.getString(STATE.getColumnName())));
283                     transaction.setVnfcName(rowSet.getString(VNFC_NAME.getColumnName()));
284                     transaction.setVserverId(rowSet.getString(VSERVER_ID.getColumnName()));
285                     transaction.setVfModuleId(rowSet.getString(VF_MODULE_ID.getColumnName()));
286                     transaction.setServiceInstanceId(rowSet.getString(SERVICE_INSTANCE_ID.getColumnName()));
287                     transaction.setMode(Flags.Mode.valueOf(rowSet.getString(MODE.getColumnName())));
288                     inProgressRecords.add(transaction);
289                 }
290             }
291             logger.trace(String.valueOf(count)
292                          + " in progress transaction records fetched from database successfully.");
293             return inProgressRecords;
294         } catch (ParseException e) {
295             logger.error("Error parsing start date during fetching in progress records ", e);
296             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
297         } catch (SQLException e) {
298             logger.error("Error fetching in progress records for Transaction ID = " + appcInstanceId + "~"
299                          + record.getTransactionId(), e);
300             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
301         }
302     }
303
304     @Override
305     public Boolean isTransactionDuplicate(TransactionRecord record) throws APPCException {
306
307         StringBuilder duplicateRequestCheckQuery = new StringBuilder()
308                 .append("SELECT ").append(TRANSACTION_ID.getColumnName()).append(" FROM ")
309                 .append(TransactionConstants.TRANSACTIONS).append(WHERE)
310                 .append(TRANSACTION_ID.getColumnName()).append(" <> ? AND ")
311                 .append(REQUEST_ID.getColumnName()).append(" = ? AND ")
312                 .append(STATE.getColumnName()).append(" IN(?,?) ");
313
314         ArrayList<String> duplicateCheckParams = new ArrayList<>();
315         duplicateCheckParams.add(appcInstanceId + "~" + record.getTransactionId());
316         duplicateCheckParams.add(record.getRequestId());
317         duplicateCheckParams.add(RequestStatus.RECEIVED.name());
318         duplicateCheckParams.add(RequestStatus.ACCEPTED.name());
319
320         if (!StringUtils.isBlank(record.getSubRequestId())) {
321             duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + " = ? ");
322             duplicateCheckParams.add(record.getSubRequestId());
323         } else {
324             duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + IS_NULL);
325         }
326         if (!StringUtils.isBlank(record.getOriginatorId())) {
327             duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + " = ? ");
328             duplicateCheckParams.add(record.getOriginatorId());
329         } else {
330             duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + IS_NULL);
331         }
332         if (logger.isDebugEnabled()) {
333             logger.debug(duplicateRequestCheckQuery.toString());
334         }
335         try (CachedRowSet rowSet =
336                 dbLibService.getData(duplicateRequestCheckQuery.toString(), duplicateCheckParams, SCHEMA)) {
337             if (rowSet != null && rowSet.first()) {
338                 String transactionId = rowSet.getString(TRANSACTION_ID.getColumnName());
339                 if (logger.isErrorEnabled()) {
340                     logger.error("Duplicate request found. Transaction ID " + transactionId
341                             + " is currently in progress.");
342                 }
343                 return true;
344             }
345             return false;
346         } catch (SQLException e) {
347             logger.error("Error checking duplicate records for Transaction ID = " + appcInstanceId + "~"
348                     + record.getTransactionId(), e);
349             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
350         }
351     }
352
353     @Override
354     public Integer getInProgressRequestsCount() throws APPCException {
355         final String inProgressRequestCountQuery =
356                 "SELECT COUNT(*) as VALUE FROM "
357                 + TransactionConstants.TRANSACTIONS
358                 + WHERE + STATE.getColumnName() + " IN (?,?) ";
359
360         ArrayList<String> checkInProgressParams = new ArrayList<>();
361         checkInProgressParams.add(RequestStatus.RECEIVED.name());
362         checkInProgressParams.add(RequestStatus.ACCEPTED.name());
363         try (CachedRowSet rowSet =
364                 dbLibService.getData(inProgressRequestCountQuery, checkInProgressParams, SCHEMA)) {
365             if (rowSet != null && rowSet.first()) {
366                 int count = rowSet.getInt("VALUE");
367                 logger.info("In progress request count fetched from database successfully.");
368                 return count;
369             }
370         }
371         catch (SQLException e) {
372             logger.error("Error checking in progress request count in the transaction table", e);
373             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
374         }
375         logger.error("Error checking in progress request count in the transaction table");
376         throw new APPCException(ERROR_ACCESSING_DATABASE);
377     }
378
379     @Override
380     public void setAppcInstanceId(String appcInstanceId) {
381         this.appcInstanceId = appcInstanceId;
382     }
383
384
385     @Override
386     public List<RequestStatus> getRecords(String requestId, String subrequestId, String originatorId, String vnfId)
387         throws APPCException {
388         StringBuilder queryString = (new StringBuilder(1024))
389             .append("SELECT ").append(TRANSACTION_ID.getColumnName())
390             .append(",")
391             .append(STATE.getColumnName())
392             .append(" FROM ").append(TRANSACTIONS)
393             .append(" WHERE ").append(TRANSACTION_ATTRIBUTES.REQUEST_ID.getColumnName()).append(" = ? AND ")
394             .append(TRANSACTION_ATTRIBUTES.TARGET_ID.getColumnName()).append(" = ?");
395         ArrayList<String> argList = new ArrayList<>();
396         argList.add(requestId);
397         argList.add(vnfId);
398
399         if (subrequestId != null) {
400             queryString.append(" AND ").append(TRANSACTION_ATTRIBUTES.SUBREQUEST_ID.getColumnName()).append(" = ?");
401             argList.add(subrequestId);
402         }
403         if (originatorId != null) {
404             queryString.append(" AND ").append(TRANSACTION_ATTRIBUTES.ORIGINATOR_ID.getColumnName()).append(" = ?");
405             argList.add(originatorId);
406         }
407
408         List<RequestStatus> requestStatusList = new ArrayList<>();
409         try (CachedRowSet resultSet = dbLibService.getData(queryString.toString(), argList, SCHEMA)) {
410             if (resultSet == null) {
411                 logger.error(String.format(
412                         "No results returned when retrieving record for requestID %s and vnfId %s %s",
413                         requestId, vnfId, "from the transactions table"));
414             } else {
415                 while (resultSet.next()) {
416                     String name = resultSet.getString(TRANSACTION_ATTRIBUTES.STATE.getColumnName());
417                     RequestStatus requestStatus = null;
418                     try {
419                         requestStatus = RequestStatus.valueOf(name);
420                     } catch (IllegalArgumentException e) {
421                         logger.error(String.format(
422                                 "Invalid request status (%s) using (%s):", name, RequestStatus.UNKNOWN), e);
423                         requestStatus = RequestStatus.UNKNOWN;
424                     }
425                     requestStatusList.add(requestStatus);
426                     logger.debug(String.format("Request Status obtained (%s).", requestStatus));
427                 }
428             }
429         } catch (SQLException e) {
430             logger.error("Error Accessing Database ", e);
431             throw new APPCException(String.format(
432                     "Error retrieving record for requestID %s and vnfId %s from the transactions table",
433                     requestId, vnfId), e);
434         }
435
436         return requestStatusList;
437     }
438
439     private ArrayList<String> prepareArguments(TransactionRecord input) {
440         ArrayList<String> arguments = new ArrayList<>();
441         arguments.add(appcInstanceId + "~" + input.getTransactionId());
442         arguments.add(dateToStringConverterMillis(input.getOriginTimestamp()));
443         arguments.add(input.getRequestId());
444         arguments.add(input.getSubRequestId());
445         arguments.add(input.getOriginatorId());
446         arguments.add(dateToStringConverterMillis(input.getStartTime()));
447         arguments.add(dateToStringConverterMillis(input.getEndTime()));
448         arguments.add(input.getTargetId());
449         arguments.add(input.getTargetType());
450         arguments.add(input.getOperation().name());
451         arguments.add(String.valueOf(input.getResultCode()));
452         arguments.add(input.getDescription());
453         arguments.add(input.getRequestState());
454         arguments.add(input.getServiceInstanceId());
455         arguments.add(input.getVnfcName());
456         arguments.add(input.getVserverId());
457         arguments.add(input.getVfModuleId());
458         arguments.add(input.getMode());
459
460         return arguments;
461     }
462
463     private static String dateToStringConverterMillis(Instant date) {
464         if (date == null) {
465             return null;
466         }
467         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
468         return formatter.format(date);
469     }
470
471     private static Instant stringToDateConverterMillis(String dateString) throws ParseException {
472         SimpleDateFormat customDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
473         return customDate.parse(dateString).toInstant();
474     }
475 }