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.
23 * ============LICENSE_END=========================================================
26 package org.onap.appc.transactionrecorder.impl;
28 import org.apache.commons.lang.StringUtils;
29 import org.onap.appc.domainmodel.lcm.Flags;
30 import org.onap.appc.domainmodel.lcm.RequestStatus;
31 import org.onap.appc.domainmodel.lcm.VNFOperation;
32 import org.onap.appc.exceptions.APPCException;
33 import org.onap.appc.transactionrecorder.TransactionRecorder;
34 import org.onap.appc.domainmodel.lcm.TransactionRecord;
35 import org.onap.appc.transactionrecorder.objects.TransactionConstants;
36 import org.onap.ccsdk.sli.core.dblib.DbLibService;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import javax.sql.rowset.CachedRowSet;
41 import java.sql.SQLException;
42 import java.text.ParseException;
43 import java.text.SimpleDateFormat;
44 import java.time.Instant;
45 import java.time.ZoneOffset;
46 import java.time.format.DateTimeFormatter;
47 import java.util.ArrayList;
48 import java.util.List;
50 import java.time.temporal.ChronoUnit;
52 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.TRANSACTION_ATTRIBUTES.*;
53 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.*;
56 public class TransactionRecorderImpl implements TransactionRecorder {
58 private final String SCHEMA = "sdnctl";
60 private String appcInstanceId;
62 private DbLibService dbLibService;
64 public void setDbLibService(DbLibService dbLibService) {
65 this.dbLibService = dbLibService;
68 private static final Logger logger = LoggerFactory.getLogger(TransactionRecorderImpl.class);
71 * Stores transaction record to appc database by calling APPC Dao layer.
73 * @param record Transaction record data.
76 public void store(TransactionRecord record) throws APPCException {
77 if (logger.isTraceEnabled()) {
78 logger.trace("Transaction data insertion into DB");
80 final String STORE_DATE_QUERY = TransactionConstants.INSERT_INTO + TransactionConstants.TRANSACTIONS +
81 "(" + TRANSACTION_ID.getColumnName() + TransactionConstants.COMMA +
82 ORIGIN_TIMESTAMP.getColumnName() + TransactionConstants.COMMA +
83 REQUEST_ID.getColumnName() + TransactionConstants.COMMA +
84 SUBREQUEST_ID.getColumnName() + TransactionConstants.COMMA +
85 ORIGINATOR_ID.getColumnName() + TransactionConstants.COMMA +
86 START_TIME.getColumnName() + TransactionConstants.COMMA +
87 END_TIME.getColumnName() + TransactionConstants.COMMA +
88 TARGET_ID.getColumnName() + TransactionConstants.COMMA +
89 TARGET_TYPE.getColumnName() + TransactionConstants.COMMA +
90 OPERATION.getColumnName() + TransactionConstants.COMMA +
91 RESULT_CODE.getColumnName() + TransactionConstants.COMMA +
92 DESCRIPTION.getColumnName() + TransactionConstants.COMMA +
93 STATE.getColumnName() + TransactionConstants.COMMA +
94 SERVICE_INSTANCE_ID + TransactionConstants.COMMA +
95 VNFC_NAME + TransactionConstants.COMMA +
96 VSERVER_ID + TransactionConstants.COMMA +
97 VF_MODULE_ID + TransactionConstants.COMMA +
99 "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
101 dbLibService.writeData(STORE_DATE_QUERY, prepareArguments(record), SCHEMA);
102 } catch (SQLException e) {
103 logger.error("Error on storing record " + record.toString(), e);
104 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
106 if (logger.isTraceEnabled()) {
107 logger.trace("Transaction Data Inserted Successfully into DB");
112 public void update(String key,
114 Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns) throws APPCException {
115 logger.debug("Inside update in TransactionRecorderImpl");
117 if (appcInstanceId != null && checkIfNullInstanceEntryExist(key, requestId)) {
118 updateNullInstanceEntry(key, requestId, updateColumns);
120 updateTransactionEntry(key, updateColumns);
122 logger.trace("End of Update Transaction table.");
125 private void updateTransactionEntry(String key, Map<TRANSACTION_ATTRIBUTES, String> updateColumns)
126 throws APPCException {
127 ArrayList<String> values = new ArrayList<>();
128 logger.debug("Inside updateTransactionEntry");
130 StringBuilder queryBuilder = new StringBuilder("UPDATE TRANSACTIONS SET ");
131 for (Map.Entry<TransactionConstants.TRANSACTION_ATTRIBUTES, String> entry : updateColumns.entrySet()) {
132 queryBuilder.append(entry.getKey().getColumnName() + " = ? ,");
133 values.add(entry.getValue());
134 logger.debug("Value for " + entry.getKey().getColumnName() + " in Transaction table: " + entry.getValue());
136 queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
137 queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ?");
138 values.add(appcInstanceId + "~" + key);
140 String query = queryBuilder.toString();
142 if (logger.isDebugEnabled()) {
143 logger.debug("Before updating Transaction table the Query is: " + query);
144 for (String value : values) {
145 logger.debug("Value for Transaction table: " + value);
148 Boolean result = dbLibService.writeData(query, values, SCHEMA);
149 logger.debug("Transactions table data update, writeData() result: " + result);
150 } catch (SQLException e) {
151 logger.error("Error in updating records in updateTransactionEntry " + e);
152 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
154 if (logger.isTraceEnabled()) {
155 logger.trace("Transaction data updated successfully");
160 private void updateNullInstanceEntry(String key,
162 Map<TRANSACTION_ATTRIBUTES, String> updateColumns) throws APPCException {
163 logger.debug("Inside updateNullInstanceEntry");
165 ArrayList<String> values = new ArrayList<>();
166 StringBuilder queryBuilder = new StringBuilder("UPDATE TRANSACTIONS SET ");
167 for (Map.Entry<TransactionConstants.TRANSACTION_ATTRIBUTES, String> entry : updateColumns.entrySet()) {
168 queryBuilder.append(entry.getKey().getColumnName() + " = ? ,");
169 values.add(entry.getValue());
171 // queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
172 queryBuilder.append(TRANSACTION_ID.getColumnName() + " = ?");
173 queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ? AND "
174 + REQUEST_ID.getColumnName() + " = ? ");
175 values.add(appcInstanceId + "~" + key);
176 values.add(null + "~" + key);
177 values.add(requestId);
178 String query = queryBuilder.toString();
180 if (logger.isDebugEnabled()) {
181 logger.debug("Before updating Transaction table the Query is: " + query);
182 for (String value : values) {
183 logger.debug("Value for Transaction table: " + value);
186 Boolean result = dbLibService.writeData(query, values, SCHEMA);
187 logger.debug("Transactions table data update, writeData() result: " + result);
189 } catch (SQLException e) {
190 logger.error("Error in updating records in updateNullInstanceEntry " + e);
191 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
196 private boolean checkIfNullInstanceEntryExist(String key, String requestId) throws APPCException {
197 logger.debug("Entered checkIfNullInstanceEntryExist");
198 String nullInstanceCheckQuery = new String("SELECT COUNT(*) as ROWCOUNT FROM " +
199 TransactionConstants.TRANSACTIONS + WHERE +
200 TRANSACTION_ID.getColumnName() + " = ? AND " +
201 REQUEST_ID.getColumnName() + " = ? ");
203 ArrayList<String> nullInstanceCheckParams = new ArrayList<>();
204 nullInstanceCheckParams.add(null + "~" + key);
205 nullInstanceCheckParams.add(requestId);
208 CachedRowSet rowSet = dbLibService.getData(nullInstanceCheckQuery, nullInstanceCheckParams, SCHEMA);
210 if (rowSet != null && rowSet.first()) {
211 noRows = rowSet.getInt("ROWCOUNT");
212 logger.info("No of Rows in Transactions Table with TRANSACTION_ID: " +
213 null + "~" + key + " and REQUEST_ID " + requestId + " is: " + noRows);
217 } catch (SQLException e) {
218 logger.error("Error in checkIfNullInstanceEntryExist in the transaction table", e);
219 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
225 public void markTransactionsAborted(String appcInstanceId) {
226 if (logger.isTraceEnabled()) {
227 logger.trace("marking in progress transactions to aborted");
229 final String updateQuery =
230 "UPDATE " + TransactionConstants.TRANSACTIONS +
231 " SET " + STATE.getColumnName() + " = '" + RequestStatus.ABORTED.name() + "',"
232 + END_TIME.getColumnName() + " = ? " +
233 WHERE + TRANSACTION_ID.getColumnName() + " LIKE '" + appcInstanceId + "%' AND "
234 + STATE.getColumnName() + " in (?,?)";
236 if (logger.isDebugEnabled()) {
237 logger.debug("Update query " + updateQuery + " appc-instance-id " + appcInstanceId);
240 ArrayList<String> arguments = new ArrayList<>();
241 arguments.add(dateToStringConverterMillis(Instant.now()));
242 arguments.add(RequestStatus.ACCEPTED.name());
243 arguments.add(RequestStatus.RECEIVED.name());
245 dbLibService.writeData(updateQuery, arguments, SCHEMA);
246 } catch (SQLException e) {
247 String message = "In progress transactions couldn't be marked aborted on server start up";
248 logger.error(message);
249 throw new RuntimeException(message, e);
251 if (logger.isTraceEnabled()) {
252 logger.trace("In progress transactions marked aborted");
257 public List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException {
259 String IN_PROGRESS_REQUESTS_QUERY = "SELECT * FROM " +
260 TransactionConstants.TRANSACTIONS + WHERE +
261 TARGET_ID + " = ? AND " +
262 STATE.getColumnName() + " IN (?,?) AND " +
263 START_TIME.getColumnName() + " < ?";
265 ArrayList<String> inProgressQueryParams = new ArrayList<>();
266 Instant window = record.getStartTime().minus(interval, ChronoUnit.HOURS);
267 inProgressQueryParams.add(record.getTargetId());
268 inProgressQueryParams.add(RequestStatus.RECEIVED.name());
269 inProgressQueryParams.add(RequestStatus.ACCEPTED.name());
270 inProgressQueryParams.add(dateToStringConverterMillis(record.getStartTime()));
272 IN_PROGRESS_REQUESTS_QUERY += " AND " + START_TIME.getColumnName() + " > ? ";
273 inProgressQueryParams.add(dateToStringConverterMillis(window));
276 try (CachedRowSet rowSet = dbLibService.getData(IN_PROGRESS_REQUESTS_QUERY, inProgressQueryParams, SCHEMA)) {
277 List<TransactionRecord> inProgressRecords = new ArrayList<>();
278 TransactionRecord transaction;
279 while (rowSet.next()) {
280 transaction = new TransactionRecord();
281 transaction.setTransactionId(rowSet.getString(TRANSACTION_ID.getColumnName()));
282 transaction.setRequestId(rowSet.getString(REQUEST_ID.getColumnName()));
283 transaction.setSubRequestId(rowSet.getString(SUBREQUEST_ID.getColumnName()));
284 transaction.setOriginatorId(rowSet.getString(ORIGINATOR_ID.getColumnName()));
285 transaction.setStartTime(stringToDateConverterMillis(rowSet.getString(START_TIME.getColumnName())));
286 transaction.setTargetId(rowSet.getString(TARGET_ID.getColumnName()));
287 transaction.setTargetType(rowSet.getString(TARGET_TYPE.getColumnName()));
288 transaction.setOperation(VNFOperation.valueOf(rowSet.getString(OPERATION.getColumnName())));
289 transaction.setRequestState(RequestStatus.valueOf(rowSet.getString(STATE.getColumnName())));
290 transaction.setVnfcName(rowSet.getString(VNFC_NAME.getColumnName()));
291 transaction.setVserverId(rowSet.getString(VSERVER_ID.getColumnName()));
292 transaction.setVfModuleId(rowSet.getString(VF_MODULE_ID.getColumnName()));
293 transaction.setServiceInstanceId(rowSet.getString(SERVICE_INSTANCE_ID.getColumnName()));
294 transaction.setMode(Flags.Mode.valueOf(rowSet.getString(MODE.getColumnName())));
295 inProgressRecords.add(transaction);
297 if (logger.isTraceEnabled()) {
298 logger.trace("In progress transaction records fetched from database successfully.");
300 return inProgressRecords;
301 } catch (ParseException e) {
302 logger.error("Error parsing start date during fetching in progress records ", e);
303 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
304 } catch (SQLException e) {
305 logger.error("Error fetching in progress records for Transaction ID = " + appcInstanceId + "~" + record
306 .getTransactionId(), e);
307 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
312 public Boolean isTransactionDuplicate(TransactionRecord record) throws APPCException {
314 StringBuilder duplicateRequestCheckQuery = new StringBuilder("SELECT " +
315 TRANSACTION_ID.getColumnName() + " FROM " +
316 TransactionConstants.TRANSACTIONS + WHERE +
317 TRANSACTION_ID.getColumnName() + " <> ? AND " +
318 REQUEST_ID.getColumnName() + " = ? AND " +
319 STATE.getColumnName() + " IN(?,?) ");
321 ArrayList<String> duplicateCheckParams = new ArrayList<>();
322 duplicateCheckParams.add(appcInstanceId + "~" + record.getTransactionId());
323 duplicateCheckParams.add(record.getRequestId());
324 duplicateCheckParams.add(RequestStatus.RECEIVED.name());
325 duplicateCheckParams.add(RequestStatus.ACCEPTED.name());
327 if (!StringUtils.isBlank(record.getSubRequestId())) {
328 duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + " = ? ");
329 duplicateCheckParams.add(record.getSubRequestId());
331 duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + IS_NULL);
333 if (!StringUtils.isBlank(record.getOriginatorId())) {
334 duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + " = ? ");
335 duplicateCheckParams.add(record.getOriginatorId());
337 duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + IS_NULL);
339 if (logger.isDebugEnabled()) {
340 logger.debug(duplicateRequestCheckQuery.toString());
342 try (CachedRowSet rowSet = dbLibService.getData(duplicateRequestCheckQuery.toString(), duplicateCheckParams,
344 if (rowSet.first()) {
345 String transactionId = rowSet.getString(TRANSACTION_ID.getColumnName());
346 if (logger.isErrorEnabled()) {
347 logger.error("Duplicate request found. Transaction ID " + transactionId + " is currently in " +
353 } catch (SQLException e) {
354 logger.error("Error checking duplicate records for Transaction ID = " + appcInstanceId + "~" + record
355 .getTransactionId(), e);
356 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
361 public Integer getInProgressRequestsCount() throws APPCException {
362 final String inProgressRequestCountQuery = "SELECT COUNT(*) as VALUE FROM "
363 + TransactionConstants.TRANSACTIONS
364 + WHERE + STATE.getColumnName() + " IN (?,?) ";
366 ArrayList<String> checkInProgressParams = new ArrayList<>();
367 checkInProgressParams.add(RequestStatus.RECEIVED.name());
368 checkInProgressParams.add(RequestStatus.ACCEPTED.name());
369 try(CachedRowSet rowSet=dbLibService.getData(inProgressRequestCountQuery,checkInProgressParams,SCHEMA)){
370 if (rowSet.first()) {
371 int count = rowSet.getInt("VALUE");
372 logger.info("In progress request count fetched from database successfully.");
376 catch (SQLException e) {
377 logger.error("Error checking in progress request count in the transaction table", e);
378 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
380 logger.error("Error checking in progress request count in the transaction table");
381 throw new APPCException(ERROR_ACCESSING_DATABASE);
385 public void setAppcInstanceId(String appcInstanceId) {
386 this.appcInstanceId = appcInstanceId;
391 public List<RequestStatus> getRecords(String requestId, String subrequestId, String originatorId, String vnfId)
392 throws APPCException {
393 StringBuilder queryString = (new StringBuilder(1024))
394 .append("SELECT " + TRANSACTION_ATTRIBUTES.STATE.getColumnName())
395 .append(" FROM " + TRANSACTIONS)
396 .append(" WHERE " + TRANSACTION_ATTRIBUTES.REQUEST_ID.getColumnName() + " = ? AND " +
397 TRANSACTION_ATTRIBUTES.TARGET_ID.getColumnName() + " = ?");
399 ArrayList<String> argList = new ArrayList<>();
400 argList.add(requestId);
403 if (subrequestId != null) {
404 queryString.append(" AND " + TRANSACTION_ATTRIBUTES.SUBREQUEST_ID.getColumnName() + " = ?");
405 argList.add(subrequestId);
407 if (originatorId != null) {
408 queryString.append(" AND " + TRANSACTION_ATTRIBUTES.ORIGINATOR_ID.getColumnName() + " = ?");
409 argList.add(originatorId);
412 List<RequestStatus> requestStatusList = new ArrayList<>();
414 CachedRowSet resultSet = dbLibService.getData(queryString.toString(), argList, SCHEMA);
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("Invalid request status (%s) using (%s) :", name, RequestStatus
423 requestStatus = RequestStatus.UNKNOWN;
425 requestStatusList.add(requestStatus);
426 logger.debug(String.format("Request Status obtained (%s).", requestStatus));
428 } catch (SQLException e) {
429 logger.error("Error Accessing Database ", e);
430 throw new APPCException(String.format("Error retrieving record for requestID %s and vnfId %s " +
431 "from the transactions table", requestId, vnfId), e);
434 return requestStatusList;
437 private ArrayList<String> prepareArguments(TransactionRecord input) {
438 ArrayList<String> arguments = new ArrayList<>();
439 arguments.add(appcInstanceId + "~" + input.getTransactionId());
440 arguments.add(dateToStringConverterMillis(input.getOriginTimestamp()));
441 arguments.add(input.getRequestId());
442 arguments.add(input.getSubRequestId());
443 arguments.add(input.getOriginatorId());
444 arguments.add(dateToStringConverterMillis(input.getStartTime()));
445 arguments.add(dateToStringConverterMillis(input.getEndTime()));
446 arguments.add(input.getTargetId());
447 arguments.add(input.getTargetType());
448 arguments.add(input.getOperation().name());
449 arguments.add(String.valueOf(input.getResultCode()));
450 arguments.add(input.getDescription());
451 arguments.add(input.getRequestState());
452 arguments.add(input.getServiceInstanceId());
453 arguments.add(input.getVnfcName());
454 arguments.add(input.getVserverId());
455 arguments.add(input.getVfModuleId());
456 arguments.add(input.getMode());
461 private static String dateToStringConverterMillis(Instant date) {
465 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
466 return formatter.format(date);
469 private static Instant stringToDateConverterMillis(String dateString) throws ParseException {
470 SimpleDateFormat customDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
471 return customDate.parse(dateString).toInstant();