98ea1b538c3c9829f78a90df6a73b47f4365fb4a
[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  * 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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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.
20  *
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.appc.transactionrecorder.impl;
25
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;
37
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;
47 import java.util.Map;
48 import java.time.temporal.ChronoUnit;
49
50 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.TRANSACTION_ATTRIBUTES.*;
51 import static org.onap.appc.transactionrecorder.objects.TransactionConstants.*;
52
53
54 public class TransactionRecorderImpl implements TransactionRecorder {
55
56     private final String SCHEMA = "sdnctl";
57
58     private String appcInstanceId;
59
60     private DbLibService dbLibService;
61
62     public void setDbLibService(DbLibService dbLibService) {
63         this.dbLibService = dbLibService;
64     }
65
66     private static final Logger logger = LoggerFactory.getLogger(TransactionRecorderImpl.class);
67
68     /**
69      * Stores transaction record to appc database by calling APPC Dao layer.
70      *
71      * @param record Transaction record data.
72      */
73     @Override
74     public void store(TransactionRecord record) throws APPCException {
75         if (logger.isTraceEnabled()) {
76             logger.trace("Transaction data insertion into DB");
77         }
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 +
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         if (logger.isTraceEnabled()) {
105             logger.trace("Transaction Data Inserted Successfully into DB");
106         }
107     }
108
109     @Override
110     public void update(String key, Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns) throws
111         APPCException {
112         ArrayList<String> values = new ArrayList<>();
113
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());
118         }
119         queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(","));
120         queryBuilder.append(WHERE + TRANSACTION_ID.getColumnName() + " = ?");
121         values.add(appcInstanceId + "~" + key);
122
123         String query = queryBuilder.toString();
124         try {
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);
129         }
130         if (logger.isTraceEnabled()) {
131             logger.trace("Transaction data updated successfully");
132         }
133
134     }
135
136     @Override
137     public void markTransactionsAborted(String appcInstanceId) {
138         if (logger.isTraceEnabled()) {
139             logger.trace("marking in progress transactions to aborted");
140         }
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 (?,?)";
147
148         if (logger.isDebugEnabled()) {
149             logger.debug("Update query " + updateQuery + " appc-instance-id " + appcInstanceId);
150         }
151
152         ArrayList<String> arguments = new ArrayList<>();
153         arguments.add(dateToStringConverterMillis(Instant.now()));
154         arguments.add(RequestStatus.ACCEPTED.name());
155         arguments.add(RequestStatus.RECEIVED.name());
156         try {
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);
162         }
163         if (logger.isTraceEnabled()) {
164             logger.trace("In progress transactions marked aborted");
165         }
166     }
167
168     @Override
169     public List<TransactionRecord> getInProgressRequests(TransactionRecord record, int interval) throws APPCException {
170
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() + " < ?";
176
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()));
183         if (interval > 0) {
184             IN_PROGRESS_REQUESTS_QUERY += " AND " + START_TIME.getColumnName() + " > ? ";
185             inProgressQueryParams.add(dateToStringConverterMillis(window));
186         }
187
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);
208             }
209             if (logger.isTraceEnabled()) {
210                 logger.trace("In progress transaction records fetched from database successfully.");
211             }
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);
220         }
221     }
222
223     @Override
224     public Boolean isTransactionDuplicate(TransactionRecord record) throws APPCException {
225
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(?,?) ");
232
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());
238
239         if (!StringUtils.isBlank(record.getSubRequestId())) {
240             duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + " = ? ");
241             duplicateCheckParams.add(record.getSubRequestId());
242         } else {
243             duplicateRequestCheckQuery.append(AND + SUBREQUEST_ID.getColumnName() + IS_NULL);
244         }
245         if (!StringUtils.isBlank(record.getOriginatorId())) {
246             duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + " = ? ");
247             duplicateCheckParams.add(record.getOriginatorId());
248         } else {
249             duplicateRequestCheckQuery.append(AND + ORIGINATOR_ID.getColumnName() + IS_NULL);
250         }
251         if (logger.isDebugEnabled()) {
252             logger.debug(duplicateRequestCheckQuery.toString());
253         }
254         try (CachedRowSet rowSet = dbLibService.getData(duplicateRequestCheckQuery.toString(), duplicateCheckParams,
255             SCHEMA)) {
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 " +
260                         "progress.");
261                 }
262                 return true;
263             }
264             return false;
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);
269         }
270     }
271
272     @Override
273     public Integer getInProgressRequestsCount() throws APPCException {
274         final String inProgressRequestCountQuery = "SELECT COUNT(*) as VALUE FROM "
275             + TransactionConstants.TRANSACTIONS
276             + WHERE + STATE.getColumnName() + " IN (?,?) ";
277
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.");
285                 return count;
286             }
287         }
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);
291         }
292         logger.error("Error checking in progress request count in the transaction table");
293         throw new APPCException(ERROR_ACCESSING_DATABASE);
294     }
295
296     @Override
297     public void setAppcInstanceId(String appcInstanceId) {
298         this.appcInstanceId = appcInstanceId;
299     }
300
301
302     @Override
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() + " = ?");
310
311         ArrayList<String> argList = new ArrayList<>();
312         argList.add(requestId);
313         argList.add(vnfId);
314
315         if (subrequestId != null) {
316             queryString.append(" AND " + TRANSACTION_ATTRIBUTES.SUBREQUEST_ID.getColumnName() + " = ?");
317             argList.add(subrequestId);
318         }
319         if (originatorId != null) {
320             queryString.append(" AND " + TRANSACTION_ATTRIBUTES.ORIGINATOR_ID.getColumnName() + " = ?");
321             argList.add(originatorId);
322         }
323
324         List<RequestStatus> requestStatusList = new ArrayList<>();
325         try {
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;
330                 try {
331                     requestStatus = RequestStatus.valueOf(name);
332                 } catch (IllegalArgumentException e) {
333                     logger.error(String.format("Invalid request status (%s) using (%s) :", name, RequestStatus
334                         .UNKNOWN), e);
335                     requestStatus = RequestStatus.UNKNOWN;
336                 }
337                 requestStatusList.add(requestStatus);
338                 logger.debug(String.format("Request Status obtained (%s).", requestStatus));
339             }
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);
344         }
345
346         return requestStatusList;
347     }
348
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());
369
370         return arguments;
371     }
372
373     private static String dateToStringConverterMillis(Instant date) {
374         if (date == null) {
375             return null;
376         }
377         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneOffset.UTC);
378         return formatter.format(date);
379     }
380
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();
384     }
385 }