2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 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
13 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * ============LICENSE_END=========================================================
24 package org.onap.appc.transactionrecorder.impl;
26 import org.apache.commons.lang.StringUtils;
27 import org.onap.appc.domainmodel.lcm.Flags;
28 import org.onap.appc.domainmodel.lcm.RequestStatus;
29 import org.onap.appc.domainmodel.lcm.VNFOperation;
30 import org.onap.appc.exceptions.APPCException;
31 import org.onap.appc.transactionrecorder.TransactionRecorder;
32 import org.onap.appc.domainmodel.lcm.TransactionRecord;
33 import org.onap.appc.transactionrecorder.objects.TransactionConstants;
34 import org.onap.ccsdk.sli.core.dblib.DbLibService;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import javax.sql.rowset.CachedRowSet;
39 import java.sql.SQLException;
40 import java.text.ParseException;
41 import java.text.SimpleDateFormat;
42 import java.time.Instant;
43 import java.time.ZoneOffset;
44 import java.time.format.DateTimeFormatter;
45 import java.util.ArrayList;
46 import java.util.List;
48 import java.time.temporal.ChronoUnit;
50 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.TRANSACTION_ATTRIBUTES.*;
51 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.*;
54 public class TransactionRecorderImpl implements TransactionRecorder {
56 private final String SCHEMA = "sdnctl";
58 private String appcInstanceId;
60 private DbLibService dbLibService;
62 public void setDbLibService(DbLibService dbLibService) {
63 this.dbLibService = dbLibService;
66 private static final Logger logger = LoggerFactory.getLogger(TransactionRecorderImpl.class);
69 * Stores transaction record to appc database by calling APPC Dao layer.
71 * @param record Transaction record data.
74 public void store(TransactionRecord record) throws APPCException {
75 if (logger.isTraceEnabled()) {
76 logger.trace("Transaction data insertion into DB");
78 final String STORE_DATE_QUERY = 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 if (logger.isTraceEnabled()) {
105 logger.trace("Transaction Data Inserted Successfully into DB");
110 public void update(String key, Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns) throws
112 ArrayList<String> values = new ArrayList<>();
114 StringBuilder queryBuilder = new StringBuilder("UPDATE TRANSACTIONS SET ");
115 for (Map.Entry<TransactionConstants.TRANSACTION_ATTRIBUTES, String> entry : updateColumns.entrySet()) {
116 queryBuilder.append(entry.getKey().getColumnName() + " = ? ,");
117 values.add(entry.getValue());
119 queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
120 queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ?");
121 values.add(appcInstanceId + "~" + key);
123 String query = queryBuilder.toString();
125 dbLibService.writeData(query, values, SCHEMA);
126 } catch (SQLException e) {
127 logger.error("Error in updating records " + e);
128 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
130 if (logger.isTraceEnabled()) {
131 logger.trace("Transaction data updated successfully");
137 public void markTransactionsAborted(String appcInstanceId) {
138 if (logger.isTraceEnabled()) {
139 logger.trace("marking in progress transactions to aborted");
141 final String updateQuery =
142 "UPDATE " + TransactionConstants.TRANSACTIONS +
143 " SET " + STATE.getColumnName() + " = '" + RequestStatus.ABORTED.name() + "',"
144 + END_TIME.getColumnName() + " = ? " +
145 WHERE + TRANSACTION_ID.getColumnName() + " LIKE '" + appcInstanceId + "%' AND "
146 + STATE.getColumnName() + " in (?,?)";
148 if (logger.isDebugEnabled()) {
149 logger.debug("Update query " + updateQuery + " appc-instance-id " + appcInstanceId);
152 ArrayList<String> arguments = new ArrayList<>();
153 arguments.add(dateToStringConverterMillis(Instant.now()));
154 arguments.add(RequestStatus.ACCEPTED.name());
155 arguments.add(RequestStatus.RECEIVED.name());
157 dbLibService.writeData(updateQuery, arguments, SCHEMA);
158 } catch (SQLException e) {
159 String message = "In progress transactions couldn't be marked aborted on server start up";
160 logger.error(message);
161 throw new RuntimeException(message);
163 if (logger.isTraceEnabled()) {
164 logger.trace("In progress transactions marked aborted");
169 public List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException {
171 String IN_PROGRESS_REQUESTS_QUERY = "SELECT * FROM " +
172 TransactionConstants.TRANSACTIONS + WHERE +
173 TARGET_ID + " = ? AND " +
174 STATE.getColumnName() + " IN (?,?) AND " +
175 START_TIME.getColumnName() + " < ?";
177 ArrayList<String> inProgressQueryParams = new ArrayList<>();
178 Instant window = record.getStartTime().minus(interval, ChronoUnit.HOURS);
179 inProgressQueryParams.add(record.getTargetId());
180 inProgressQueryParams.add(RequestStatus.RECEIVED.name());
181 inProgressQueryParams.add(RequestStatus.ACCEPTED.name());
182 inProgressQueryParams.add(dateToStringConverterMillis(record.getStartTime()));
184 IN_PROGRESS_REQUESTS_QUERY += " AND " + START_TIME.getColumnName() + " > ? ";
185 inProgressQueryParams.add(dateToStringConverterMillis(window));
188 try (CachedRowSet rowSet = dbLibService.getData(IN_PROGRESS_REQUESTS_QUERY, inProgressQueryParams, SCHEMA)) {
189 List<TransactionRecord> inProgressRecords = new ArrayList<>();
190 TransactionRecord transaction;
191 while (rowSet.next()) {
192 transaction = new TransactionRecord();
193 transaction.setTransactionId(rowSet.getString(TRANSACTION_ID.getColumnName()));
194 transaction.setRequestId(rowSet.getString(REQUEST_ID.getColumnName()));
195 transaction.setSubRequestId(rowSet.getString(SUBREQUEST_ID.getColumnName()));
196 transaction.setOriginatorId(rowSet.getString(ORIGINATOR_ID.getColumnName()));
197 transaction.setStartTime(stringToDateConverterMillis(rowSet.getString(START_TIME.getColumnName())));
198 transaction.setTargetId(rowSet.getString(TARGET_ID.getColumnName()));
199 transaction.setTargetType(rowSet.getString(TARGET_TYPE.getColumnName()));
200 transaction.setOperation(VNFOperation.valueOf(rowSet.getString(OPERATION.getColumnName())));
201 transaction.setRequestState(RequestStatus.valueOf(rowSet.getString(STATE.getColumnName())));
202 transaction.setVnfcName(rowSet.getString(VNFC_NAME.getColumnName()));
203 transaction.setVserverId(rowSet.getString(VSERVER_ID.getColumnName()));
204 transaction.setVfModuleId(rowSet.getString(VF_MODULE_ID.getColumnName()));
205 transaction.setServiceInstanceId(rowSet.getString(SERVICE_INSTANCE_ID.getColumnName()));
206 transaction.setMode(Flags.Mode.valueOf(rowSet.getString(MODE.getColumnName())));
207 inProgressRecords.add(transaction);
209 if (logger.isTraceEnabled()) {
210 logger.trace("In progress transaction records fetched from database successfully.");
212 return inProgressRecords;
213 } catch (ParseException e) {
214 logger.error("Error parsing start date during fetching in progress records ", e);
215 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
216 } catch (SQLException e) {
217 logger.error("Error fetching in progress records for Transaction ID = " + appcInstanceId + "~" + record
218 .getTransactionId(), e);
219 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
224 public Boolean isTransactionDuplicate(TransactionRecord record) throws APPCException {
226 StringBuilder duplicateRequestCheckQuery = new StringBuilder("SELECT " +
227 TRANSACTION_ID.getColumnName() + " FROM " +
228 TransactionConstants.TRANSACTIONS + WHERE +
229 TRANSACTION_ID.getColumnName() + " <> ? AND " +
230 REQUEST_ID.getColumnName() + " = ? AND " +
231 STATE.getColumnName() + " IN(?,?) ");
233 ArrayList<String> duplicateCheckParams = new ArrayList<>();
234 duplicateCheckParams.add(appcInstanceId + "~" + record.getTransactionId());
235 duplicateCheckParams.add(record.getRequestId());
236 duplicateCheckParams.add(RequestStatus.RECEIVED.name());
237 duplicateCheckParams.add(RequestStatus.ACCEPTED.name());
239 if (!StringUtils.isBlank(record.getSubRequestId())) {
240 duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + " = ? ");
241 duplicateCheckParams.add(record.getSubRequestId());
243 duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + IS_NULL);
245 if (!StringUtils.isBlank(record.getOriginatorId())) {
246 duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + " = ? ");
247 duplicateCheckParams.add(record.getOriginatorId());
249 duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + IS_NULL);
251 if (logger.isDebugEnabled()) {
252 logger.debug(duplicateRequestCheckQuery.toString());
254 try (CachedRowSet rowSet = dbLibService.getData(duplicateRequestCheckQuery.toString(), duplicateCheckParams,
256 if (rowSet.first()) {
257 String transactionId = rowSet.getString(TRANSACTION_ID.getColumnName());
258 if (logger.isErrorEnabled()) {
259 logger.error("Duplicate request found. Transaction ID " + transactionId + " is currently in " +
265 } catch (SQLException e) {
266 logger.error("Error checking duplicate records for Transaction ID = " + appcInstanceId + "~" + record
267 .getTransactionId(), e);
268 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
273 public Integer getInProgressRequestsCount() throws APPCException {
274 final String inProgressRequestCountQuery = "SELECT COUNT(*) as VALUE FROM "
275 + TransactionConstants.TRANSACTIONS
276 + WHERE + STATE.getColumnName() + " IN (?,?) ";
278 ArrayList<String> checkInProgressParams = new ArrayList<>();
279 checkInProgressParams.add(RequestStatus.RECEIVED.name());
280 checkInProgressParams.add(RequestStatus.ACCEPTED.name());
281 try(CachedRowSet rowSet=dbLibService.getData(inProgressRequestCountQuery,checkInProgressParams,SCHEMA)){
282 if (rowSet.first()) {
283 int count = rowSet.getInt("VALUE");
284 logger.info("In progress request count fetched from database successfully.");
288 catch (SQLException e) {
289 logger.error("Error checking in progress request count in the transaction table", e);
290 throw new APPCException(ERROR_ACCESSING_DATABASE, e);
292 logger.error("Error checking in progress request count in the transaction table");
293 throw new APPCException(ERROR_ACCESSING_DATABASE);
297 public void setAppcInstanceId(String appcInstanceId) {
298 this.appcInstanceId = appcInstanceId;
303 public List<RequestStatus> getRecords(String requestId, String subrequestId, String originatorId, String vnfId)
304 throws APPCException {
305 StringBuilder queryString = (new StringBuilder(1024))
306 .append("SELECT " + TRANSACTION_ATTRIBUTES.STATE.getColumnName())
307 .append(" FROM " + TRANSACTIONS)
308 .append(" WHERE " + TRANSACTION_ATTRIBUTES.REQUEST_ID.getColumnName() + " = ? AND " +
309 TRANSACTION_ATTRIBUTES.TARGET_ID.getColumnName() + " = ?");
311 ArrayList<String> argList = new ArrayList<>();
312 argList.add(requestId);
315 if (subrequestId != null) {
316 queryString.append(" AND " + TRANSACTION_ATTRIBUTES.SUBREQUEST_ID.getColumnName() + " = ?");
317 argList.add(subrequestId);
319 if (originatorId != null) {
320 queryString.append(" AND " + TRANSACTION_ATTRIBUTES.ORIGINATOR_ID.getColumnName() + " = ?");
321 argList.add(originatorId);
324 List<RequestStatus> requestStatusList = new ArrayList<>();
326 CachedRowSet resultSet = dbLibService.getData(queryString.toString(), argList, SCHEMA);
327 while (resultSet.next()) {
328 String name = resultSet.getString(TRANSACTION_ATTRIBUTES.STATE.getColumnName());
329 RequestStatus requestStatus = null;
331 requestStatus = RequestStatus.valueOf(name);
332 } catch (IllegalArgumentException e) {
333 logger.error(String.format("Invalid request status (%s) using (%s) :", name, RequestStatus
335 requestStatus = RequestStatus.UNKNOWN;
337 requestStatusList.add(requestStatus);
338 logger.debug(String.format("Request Status obtained (%s).", requestStatus));
340 } catch (SQLException e) {
341 logger.error("Error Accessing Database ", e);
342 throw new APPCException(String.format("Error retrieving record for requestID %s and vnfId %s " +
343 "from the transactions table", requestId, vnfId), e);
346 return requestStatusList;
349 private ArrayList<String> prepareArguments(TransactionRecord input) {
350 ArrayList<String> arguments = new ArrayList<>();
351 arguments.add(appcInstanceId + "~" + input.getTransactionId());
352 arguments.add(dateToStringConverterMillis(input.getOriginTimestamp()));
353 arguments.add(input.getRequestId());
354 arguments.add(input.getSubRequestId());
355 arguments.add(input.getOriginatorId());
356 arguments.add(dateToStringConverterMillis(input.getStartTime()));
357 arguments.add(dateToStringConverterMillis(input.getEndTime()));
358 arguments.add(input.getTargetId());
359 arguments.add(input.getTargetType());
360 arguments.add(input.getOperation().name());
361 arguments.add(String.valueOf(input.getResultCode()));
362 arguments.add(input.getDescription());
363 arguments.add(input.getRequestState());
364 arguments.add(input.getServiceInstanceId());
365 arguments.add(input.getVnfcName());
366 arguments.add(input.getVserverId());
367 arguments.add(input.getVfModuleId());
368 arguments.add(input.getMode());
373 private static String dateToStringConverterMillis(Instant date) {
377 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
378 return formatter.format(date);
381 private static Instant stringToDateConverterMillis(String dateString) throws ParseException {
382 SimpleDateFormat customDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
383 return customDate.parse(dateString).toInstant();