2 * ============LICENSE_START=======================================================
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
15 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
25 package org.onap.appc.transactionrecorder.impl;
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;
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;
51 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.TRANSACTION_ATTRIBUTES.*;
52 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.*;
55 public class TransactionRecorderImpl implements TransactionRecorder {
57 private final String SCHEMA = "sdnctl";
59 private String appcInstanceId;
61 private DbLibService dbLibService;
63 public void setDbLibService(DbLibService dbLibService) {
64 this.dbLibService = dbLibService;
67 private static final Logger logger = LoggerFactory.getLogger(TransactionRecorderImpl.class);
70 * Stores transaction record to appc database by calling APPC Dao layer.
72 * @param record Transaction record data.
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
97 + "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
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);
104 logger.trace("Transaction Data Inserted Successfully into DB");
108 public void update(String key,
110 Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns) throws APPCException {
111 logger.debug("Inside update in TransactionRecorderImpl");
113 if (appcInstanceId != null && checkIfNullInstanceEntryExist(key, requestId)) {
114 updateNullInstanceEntry(key, requestId, updateColumns);
116 updateTransactionEntry(key, updateColumns);
118 logger.trace("End of Update Transaction table.");
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");
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());
132 queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
133 queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ?");
134 values.add(appcInstanceId + "~" + key);
136 String query = queryBuilder.toString();
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);
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);
150 logger.trace("Transaction data updated successfully");
153 private void updateNullInstanceEntry(String key,
155 Map<TRANSACTION_ATTRIBUTES, String> updateColumns) throws APPCException {
156 logger.debug("Inside updateNullInstanceEntry");
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());
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();
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);
179 Boolean result = dbLibService.writeData(query, values, SCHEMA);
180 logger.debug("Transactions table data update, writeData() result: " + result);
182 } catch (SQLException e) {
183 logger.error("Error in updating records in updateNullInstanceEntry " + e);
184 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
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() + " = ? ");
197 ArrayList<String> nullInstanceCheckParams = new ArrayList<>();
198 nullInstanceCheckParams.add(null + "~" + key);
199 nullInstanceCheckParams.add(requestId);
201 try (CachedRowSet rowSet = dbLibService.getData(nullInstanceCheckQuery, nullInstanceCheckParams, SCHEMA)) {
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);
210 } catch (SQLException e) {
211 logger.error("Error in checkIfNullInstanceEntryExist in the transaction table", e);
212 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
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 (?,?)";
227 if (logger.isDebugEnabled()) {
228 logger.debug("Update query " + updateQuery + " appc-instance-id " + appcInstanceId);
231 ArrayList<String> arguments = new ArrayList<>();
232 arguments.add(dateToStringConverterMillis(Instant.now()));
233 arguments.add(RequestStatus.ACCEPTED.name());
234 arguments.add(RequestStatus.RECEIVED.name());
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);
242 logger.trace("In progress transactions marked aborted");
246 public List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException {
248 String IN_PROGRESS_REQUESTS_QUERY =
250 + TransactionConstants.TRANSACTIONS + WHERE
251 + TARGET_ID + " = ? AND "
252 + STATE.getColumnName() + " IN (?,?) AND "
253 + START_TIME.getColumnName() + " < ?";
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()));
262 IN_PROGRESS_REQUESTS_QUERY += " AND " + START_TIME.getColumnName() + " > ? ";
263 inProgressQueryParams.add(dateToStringConverterMillis(window));
266 try (CachedRowSet rowSet =
267 dbLibService.getData(IN_PROGRESS_REQUESTS_QUERY, inProgressQueryParams, SCHEMA)) {
268 List<TransactionRecord> inProgressRecords = new ArrayList<>();
269 TransactionRecord transaction;
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);
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);
305 public Boolean isTransactionDuplicate(TransactionRecord record) throws APPCException {
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(?,?) ");
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());
320 if (!StringUtils.isBlank(record.getSubRequestId())) {
321 duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + " = ? ");
322 duplicateCheckParams.add(record.getSubRequestId());
324 duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + IS_NULL);
326 if (!StringUtils.isBlank(record.getOriginatorId())) {
327 duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + " = ? ");
328 duplicateCheckParams.add(record.getOriginatorId());
330 duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + IS_NULL);
332 if (logger.isDebugEnabled()) {
333 logger.debug(duplicateRequestCheckQuery.toString());
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.");
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);
354 public Integer getInProgressRequestsCount() throws APPCException {
355 final String inProgressRequestCountQuery =
356 "SELECT COUNT(*) as VALUE FROM "
357 + TransactionConstants.TRANSACTIONS
358 + WHERE + STATE.getColumnName() + " IN (?,?) ";
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.");
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);
375 logger.error("Error checking in progress request count in the transaction table");
376 throw new APPCException(ERROR_ACCESSING_DATABASE);
380 public void setAppcInstanceId(String appcInstanceId) {
381 this.appcInstanceId = appcInstanceId;
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())
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);
399 if (subrequestId != null) {
400 queryString.append(" AND ").append(TRANSACTION_ATTRIBUTES.SUBREQUEST_ID.getColumnName()).append(" = ?");
401 argList.add(subrequestId);
403 if (originatorId != null) {
404 queryString.append(" AND ").append(TRANSACTION_ATTRIBUTES.ORIGINATOR_ID.getColumnName()).append(" = ?");
405 argList.add(originatorId);
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"));
415 while (resultSet.next()) {
416 String name = resultSet.getString(TRANSACTION_ATTRIBUTES.STATE.getColumnName());
417 RequestStatus requestStatus = null;
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;
425 requestStatusList.add(requestStatus);
426 logger.debug(String.format("Request Status obtained (%s).", requestStatus));
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);
436 return requestStatusList;
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());
463 private static String dateToStringConverterMillis(Instant date) {
467 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
468 return formatter.format(date);
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();