5c5d8fc24a7728e49e45e13fde11f6c1fd5b3e03
[appc.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : APPC
4  * ================================================================================
5  * Copyright (C) 2017-2018 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  *
23  * ============LICENSE_END=========================================================
24  */
25
26 package org.onap.appc.transactionrecorder.impl;
27
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;
39
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;
49 import java.util.Map;
50 import java.time.temporal.ChronoUnit;
51
52 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.TRANSACTION_ATTRIBUTES.*;
53 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.*;
54
55
56 public class TransactionRecorderImpl implements TransactionRecorder {
57
58     private final String SCHEMA = "sdnctl";
59
60     private String appcInstanceId;
61
62     private DbLibService dbLibService;
63
64     public void setDbLibService(DbLibService dbLibService) {
65         this.dbLibService = dbLibService;
66     }
67
68     private static final Logger logger = LoggerFactory.getLogger(TransactionRecorderImpl.class);
69
70     /**
71      * Stores transaction record to appc database by calling APPC Dao layer.
72      *
73      * @param record Transaction record data.
74      */
75     @Override
76     public void store(TransactionRecord record) throws APPCException {
77         if (logger.isTraceEnabled()) {
78             logger.trace("Transaction data insertion into DB");
79         }
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 +
98             MODE + ") " +
99             "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
100         try {
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);
105         }
106         if (logger.isTraceEnabled()) {
107             logger.trace("Transaction Data Inserted Successfully into DB");
108         }
109     }
110
111     @Override
112     public void update(String key, Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns) throws
113         APPCException {
114         ArrayList<String> values = new ArrayList<>();
115
116         StringBuilder queryBuilder = new StringBuilder("UPDATE TRANSACTIONS SET ");
117         for (Map.Entry<TransactionConstants.TRANSACTION_ATTRIBUTES, String> entry : updateColumns.entrySet()) {
118             queryBuilder.append(entry.getKey().getColumnName() + " = ? ,");
119             values.add(entry.getValue());
120         }
121         queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
122         queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ?");
123         values.add(appcInstanceId + "~" + key);
124
125         String query = queryBuilder.toString();
126         try {
127             dbLibService.writeData(query, values, SCHEMA);
128         } catch (SQLException e) {
129             logger.error("Error in updating records " + e);
130             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
131         }
132         if (logger.isTraceEnabled()) {
133             logger.trace("Transaction data updated successfully");
134         }
135
136     }
137
138     @Override
139     public void markTransactionsAborted(String appcInstanceId) {
140         if (logger.isTraceEnabled()) {
141             logger.trace("marking in progress transactions to aborted");
142         }
143         final String updateQuery =
144             "UPDATE " + TransactionConstants.TRANSACTIONS +
145                 " SET " + STATE.getColumnName() + " = '" + RequestStatus.ABORTED.name() + "',"
146                         + END_TIME.getColumnName() + " = ? " +
147                 WHERE + TRANSACTION_ID.getColumnName() + " LIKE '" + appcInstanceId + "%'  AND "
148                 + STATE.getColumnName() + " in (?,?)";
149
150         if (logger.isDebugEnabled()) {
151             logger.debug("Update query " + updateQuery + " appc-instance-id " + appcInstanceId);
152         }
153
154         ArrayList<String> arguments = new ArrayList<>();
155         arguments.add(dateToStringConverterMillis(Instant.now()));
156         arguments.add(RequestStatus.ACCEPTED.name());
157         arguments.add(RequestStatus.RECEIVED.name());
158         try {
159             dbLibService.writeData(updateQuery, arguments, SCHEMA);
160         } catch (SQLException e) {
161             String message = "In progress transactions couldn't be marked aborted on server start up";
162             logger.error(message);
163             throw new RuntimeException(message, e);
164         }
165         if (logger.isTraceEnabled()) {
166             logger.trace("In progress transactions marked aborted");
167         }
168     }
169
170     @Override
171     public List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException {
172
173         String IN_PROGRESS_REQUESTS_QUERY = "SELECT * FROM " +
174             TransactionConstants.TRANSACTIONS + WHERE +
175             TARGET_ID + " = ? AND " +
176             STATE.getColumnName() + " IN (?,?) AND " +
177             START_TIME.getColumnName() + " < ?";
178
179         ArrayList<String> inProgressQueryParams = new ArrayList<>();
180         Instant window = record.getStartTime().minus(interval, ChronoUnit.HOURS);
181         inProgressQueryParams.add(record.getTargetId());
182         inProgressQueryParams.add(RequestStatus.RECEIVED.name());
183         inProgressQueryParams.add(RequestStatus.ACCEPTED.name());
184         inProgressQueryParams.add(dateToStringConverterMillis(record.getStartTime()));
185         if (interval > 0) {
186             IN_PROGRESS_REQUESTS_QUERY += " AND " + START_TIME.getColumnName() + " > ? ";
187             inProgressQueryParams.add(dateToStringConverterMillis(window));
188         }
189
190         try (CachedRowSet rowSet = dbLibService.getData(IN_PROGRESS_REQUESTS_QUERY, inProgressQueryParams, SCHEMA)) {
191             List<TransactionRecord> inProgressRecords = new ArrayList<>();
192             TransactionRecord transaction;
193             while (rowSet.next()) {
194                 transaction = new TransactionRecord();
195                 transaction.setTransactionId(rowSet.getString(TRANSACTION_ID.getColumnName()));
196                 transaction.setRequestId(rowSet.getString(REQUEST_ID.getColumnName()));
197                 transaction.setSubRequestId(rowSet.getString(SUBREQUEST_ID.getColumnName()));
198                 transaction.setOriginatorId(rowSet.getString(ORIGINATOR_ID.getColumnName()));
199                 transaction.setStartTime(stringToDateConverterMillis(rowSet.getString(START_TIME.getColumnName())));
200                 transaction.setTargetId(rowSet.getString(TARGET_ID.getColumnName()));
201                 transaction.setTargetType(rowSet.getString(TARGET_TYPE.getColumnName()));
202                 transaction.setOperation(VNFOperation.valueOf(rowSet.getString(OPERATION.getColumnName())));
203                 transaction.setRequestState(RequestStatus.valueOf(rowSet.getString(STATE.getColumnName())));
204                 transaction.setVnfcName(rowSet.getString(VNFC_NAME.getColumnName()));
205                 transaction.setVserverId(rowSet.getString(VSERVER_ID.getColumnName()));
206                 transaction.setVfModuleId(rowSet.getString(VF_MODULE_ID.getColumnName()));
207                 transaction.setServiceInstanceId(rowSet.getString(SERVICE_INSTANCE_ID.getColumnName()));
208                 transaction.setMode(Flags.Mode.valueOf(rowSet.getString(MODE.getColumnName())));
209                 inProgressRecords.add(transaction);
210             }
211             if (logger.isTraceEnabled()) {
212                 logger.trace("In progress transaction records fetched from database successfully.");
213             }
214             return inProgressRecords;
215         } catch (ParseException e) {
216             logger.error("Error parsing start date during fetching in progress records ", e);
217             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
218         } catch (SQLException e) {
219             logger.error("Error fetching in progress records for Transaction ID = " + appcInstanceId + "~" + record
220                 .getTransactionId(), e);
221             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
222         }
223     }
224
225     @Override
226     public Boolean isTransactionDuplicate(TransactionRecord record) throws APPCException {
227
228         StringBuilder duplicateRequestCheckQuery = new StringBuilder("SELECT " +
229             TRANSACTION_ID.getColumnName() + " FROM " +
230             TransactionConstants.TRANSACTIONS + WHERE +
231             TRANSACTION_ID.getColumnName() + " <> ? AND " +
232             REQUEST_ID.getColumnName() + " = ? AND " +
233             STATE.getColumnName() + " IN(?,?) ");
234
235         ArrayList<String> duplicateCheckParams = new ArrayList<>();
236         duplicateCheckParams.add(appcInstanceId + "~" + record.getTransactionId());
237         duplicateCheckParams.add(record.getRequestId());
238         duplicateCheckParams.add(RequestStatus.RECEIVED.name());
239         duplicateCheckParams.add(RequestStatus.ACCEPTED.name());
240
241         if (!StringUtils.isBlank(record.getSubRequestId())) {
242             duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + " = ? ");
243             duplicateCheckParams.add(record.getSubRequestId());
244         } else {
245             duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + IS_NULL);
246         }
247         if (!StringUtils.isBlank(record.getOriginatorId())) {
248             duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + " = ? ");
249             duplicateCheckParams.add(record.getOriginatorId());
250         } else {
251             duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + IS_NULL);
252         }
253         if (logger.isDebugEnabled()) {
254             logger.debug(duplicateRequestCheckQuery.toString());
255         }
256         try (CachedRowSet rowSet = dbLibService.getData(duplicateRequestCheckQuery.toString(), duplicateCheckParams,
257             SCHEMA)) {
258             if (rowSet.first()) {
259                 String transactionId = rowSet.getString(TRANSACTION_ID.getColumnName());
260                 if (logger.isErrorEnabled()) {
261                     logger.error("Duplicate request found. Transaction ID " + transactionId + " is currently in " +
262                         "progress.");
263                 }
264                 return true;
265             }
266             return false;
267         } catch (SQLException e) {
268             logger.error("Error checking duplicate records for Transaction ID = " + appcInstanceId + "~" + record
269                 .getTransactionId(), e);
270             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
271         }
272     }
273
274     @Override
275     public Integer getInProgressRequestsCount() throws APPCException {
276         final String inProgressRequestCountQuery = "SELECT COUNT(*) as VALUE FROM "
277             + TransactionConstants.TRANSACTIONS
278             + WHERE + STATE.getColumnName() + " IN (?,?) ";
279
280         ArrayList<String> checkInProgressParams = new ArrayList<>();
281         checkInProgressParams.add(RequestStatus.RECEIVED.name());
282         checkInProgressParams.add(RequestStatus.ACCEPTED.name());
283         try(CachedRowSet rowSet=dbLibService.getData(inProgressRequestCountQuery,checkInProgressParams,SCHEMA)){
284             if (rowSet.first()) {
285                 int count = rowSet.getInt("VALUE");
286                 logger.info("In progress request count fetched from database successfully.");
287                 return count;
288             }
289         }
290         catch (SQLException e) {
291             logger.error("Error checking in progress request count in the transaction table", e);
292             throw new APPCException(ERROR_ACCESSING_DATABASE, e);
293         }
294         logger.error("Error checking in progress request count in the transaction table");
295         throw new APPCException(ERROR_ACCESSING_DATABASE);
296     }
297
298     @Override
299     public void setAppcInstanceId(String appcInstanceId) {
300         this.appcInstanceId = appcInstanceId;
301     }
302
303
304     @Override
305     public List<RequestStatus> getRecords(String requestId, String subrequestId, String originatorId, String vnfId)
306         throws APPCException {
307         StringBuilder queryString = (new StringBuilder(1024))
308             .append("SELECT " + TRANSACTION_ATTRIBUTES.STATE.getColumnName())
309             .append(" FROM " + TRANSACTIONS)
310             .append(" WHERE " + TRANSACTION_ATTRIBUTES.REQUEST_ID.getColumnName() + "  = ? AND " +
311                 TRANSACTION_ATTRIBUTES.TARGET_ID.getColumnName() + " = ?");
312
313         ArrayList<String> argList = new ArrayList<>();
314         argList.add(requestId);
315         argList.add(vnfId);
316
317         if (subrequestId != null) {
318             queryString.append(" AND " + TRANSACTION_ATTRIBUTES.SUBREQUEST_ID.getColumnName() + " = ?");
319             argList.add(subrequestId);
320         }
321         if (originatorId != null) {
322             queryString.append(" AND " + TRANSACTION_ATTRIBUTES.ORIGINATOR_ID.getColumnName() + " = ?");
323             argList.add(originatorId);
324         }
325
326         List<RequestStatus> requestStatusList = new ArrayList<>();
327         try {
328             CachedRowSet resultSet = dbLibService.getData(queryString.toString(), argList, SCHEMA);
329             while (resultSet.next()) {
330                 String name = resultSet.getString(TRANSACTION_ATTRIBUTES.STATE.getColumnName());
331                 RequestStatus requestStatus = null;
332                 try {
333                     requestStatus = RequestStatus.valueOf(name);
334                 } catch (IllegalArgumentException e) {
335                     logger.error(String.format("Invalid request status (%s) using (%s) :", name, RequestStatus
336                         .UNKNOWN), e);
337                     requestStatus = RequestStatus.UNKNOWN;
338                 }
339                 requestStatusList.add(requestStatus);
340                 logger.debug(String.format("Request Status obtained (%s).", requestStatus));
341             }
342         } catch (SQLException e) {
343             logger.error("Error Accessing Database ", e);
344             throw new APPCException(String.format("Error retrieving record for requestID %s and vnfId %s " +
345                 "from the transactions table", requestId, vnfId), e);
346         }
347
348         return requestStatusList;
349     }
350
351     private ArrayList<String> prepareArguments(TransactionRecord input) {
352         ArrayList<String> arguments = new ArrayList<>();
353         arguments.add(appcInstanceId + "~" + input.getTransactionId());
354         arguments.add(dateToStringConverterMillis(input.getOriginTimestamp()));
355         arguments.add(input.getRequestId());
356         arguments.add(input.getSubRequestId());
357         arguments.add(input.getOriginatorId());
358         arguments.add(dateToStringConverterMillis(input.getStartTime()));
359         arguments.add(dateToStringConverterMillis(input.getEndTime()));
360         arguments.add(input.getTargetId());
361         arguments.add(input.getTargetType());
362         arguments.add(input.getOperation().name());
363         arguments.add(String.valueOf(input.getResultCode()));
364         arguments.add(input.getDescription());
365         arguments.add(input.getRequestState());
366         arguments.add(input.getServiceInstanceId());
367         arguments.add(input.getVnfcName());
368         arguments.add(input.getVserverId());
369         arguments.add(input.getVfModuleId());
370         arguments.add(input.getMode());
371
372         return arguments;
373     }
374
375     private static String dateToStringConverterMillis(Instant date) {
376         if (date == null) {
377             return null;
378         }
379         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
380         return formatter.format(date);
381     }
382
383     private static Instant stringToDateConverterMillis(String dateString) throws ParseException {
384         SimpleDateFormat customDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
385         return customDate.parse(dateString).toInstant();
386     }
387 }