18fa8a18fa6cdc4dd68a98a31b50f2549f686ae0
[music.git] / music-rest / src / main / java / org / onap / music / conductor / conditionals / MusicConditional.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
6  * ===================================================================
7  *  Modifications Copyright (C) 2019 IBM.
8  *  Modifications Copyright (c) 2019 Samsung
9  * ===================================================================
10  *  Licensed under the Apache License, Version 2.0 (the "License");
11  *  you may not use this file except in compliance with the License.
12  *  You may obtain a copy of the License at
13  * 
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  * 
16  *  Unless required by applicable law or agreed to in writing, software
17  *  distributed under the License is distributed on an "AS IS" BASIS,
18  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  *  See the License for the specific language governing permissions and
20  *  limitations under the License.
21  * 
22  * ============LICENSE_END=============================================
23  * ====================================================================
24  */
25
26 package org.onap.music.conductor.conditionals;
27
28 import java.io.PrintWriter;
29 import java.io.StringWriter;
30 import java.util.HashMap;
31 import java.util.Map;
32
33 import org.codehaus.jettison.json.JSONObject;
34 import org.onap.music.datastore.MusicDataStoreHandle;
35 import org.onap.music.datastore.PreparedQueryObject;
36 import org.onap.music.eelf.logging.EELFLoggerDelegate;
37 import org.onap.music.eelf.logging.format.AppMessages;
38 import org.onap.music.eelf.logging.format.ErrorSeverity;
39 import org.onap.music.eelf.logging.format.ErrorTypes;
40 import org.onap.music.exceptions.MusicLockingException;
41 import org.onap.music.exceptions.MusicQueryException;
42 import org.onap.music.exceptions.MusicServiceException;
43 import org.onap.music.main.MusicCore;
44 import org.onap.music.main.MusicUtil;
45 import org.onap.music.main.ResultType;
46 import org.onap.music.main.ReturnType;
47 import org.onap.music.rest.RestMusicDataAPI;
48
49 import com.datastax.driver.core.ColumnDefinitions;
50 import com.datastax.driver.core.DataType;
51 import com.datastax.driver.core.ResultSet;
52 import com.datastax.driver.core.Row;
53 import com.datastax.driver.core.TableMetadata;
54
55 public class MusicConditional {
56     private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(RestMusicDataAPI.class);
57
58     public static ReturnType conditionalInsert(String keyspace, String tablename, String casscadeColumnName,
59             Map<String, Object> casscadeColumnData, String primaryKey, Map<String, Object> valuesMap,
60             Map<String, String> status) throws Exception {
61
62         Map<String, PreparedQueryObject> queryBank = new HashMap<>();
63         TableMetadata tableInfo = null;
64         tableInfo = MusicDataStoreHandle.returnColumnMetadata(keyspace, tablename);
65         DataType primaryIdType = tableInfo.getPrimaryKey().get(0).getType();
66         String primaryId = tableInfo.getPrimaryKey().get(0).getName();
67         DataType casscadeColumnType = tableInfo.getColumn(casscadeColumnName).getType();
68         String vector = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
69
70         PreparedQueryObject select = new PreparedQueryObject();
71         select.appendQueryString("SELECT * FROM " + keyspace + "." + tablename + " where " + primaryId + " = ?");
72         select.addValue(MusicUtil.convertToActualDataType(primaryIdType, primaryKey));
73         queryBank.put(MusicUtil.SELECT, select);
74
75         PreparedQueryObject update = new PreparedQueryObject();
76         //casscade column values
77         Map<String, String> updateColumnvalues = getValues(true, casscadeColumnData, status);
78         Object formatedValues = MusicUtil.convertToActualDataType(casscadeColumnType, updateColumnvalues);
79         update.appendQueryString("UPDATE " + keyspace + "." + tablename + " SET " + casscadeColumnName + " ="
80                 + casscadeColumnName + " + ? , vector_ts = ?" + " WHERE " + primaryId + " = ? ");
81         update.addValue(formatedValues);
82         update.addValue(MusicUtil.convertToActualDataType(DataType.text(), vector));
83         update.addValue(MusicUtil.convertToActualDataType(primaryIdType, primaryKey));
84         queryBank.put(MusicUtil.UPDATE, update);
85
86
87         //casscade column values
88         Map<String, String> insertColumnvalues = getValues(false, casscadeColumnData, status);
89         formatedValues = MusicUtil.convertToActualDataType(casscadeColumnType, insertColumnvalues);
90         PreparedQueryObject insert = extractQuery(valuesMap, tableInfo, tablename, keyspace, primaryId, primaryKey,casscadeColumnName,formatedValues);
91         queryBank.put(MusicUtil.INSERT, insert);
92         
93         
94         String key = keyspace + "." + tablename + "." + primaryKey;
95         String lockId;
96         try {
97                 lockId = MusicCore.createLockReference(key);
98         } catch (MusicLockingException e) {
99                 return new ReturnType(ResultType.FAILURE, e.getMessage());
100         }
101         long leasePeriod = MusicUtil.getDefaultLockLeasePeriod();
102         ReturnType lockAcqResult = MusicCore.acquireLockWithLease(key, lockId, leasePeriod);
103
104         try {
105             if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) {
106                 ReturnType criticalPutResult = conditionalInsertAtomic(lockId, keyspace, tablename, primaryKey,
107                         queryBank);
108                 MusicCore.destroyLockRef(lockId);
109                 if (criticalPutResult.getMessage().contains("insert"))
110                     criticalPutResult
111                             .setMessage("Insert values: ");
112                 else if (criticalPutResult.getMessage().contains("update"))
113                     criticalPutResult
114                             .setMessage("Update values: " + updateColumnvalues);
115                 return criticalPutResult;
116
117             } else {
118                 MusicCore.destroyLockRef(lockId);
119                 return lockAcqResult;
120             }
121         } catch (Exception e) {
122             logger.error(EELFLoggerDelegate.applicationLogger, e);
123             MusicCore.destroyLockRef(lockId);
124             return new ReturnType(ResultType.FAILURE, e.getMessage());
125         }
126
127     }
128
129     public static ReturnType conditionalInsertAtomic(String lockId, String keyspace, String tableName,
130             String primaryKey, Map<String, PreparedQueryObject> queryBank) {
131
132         ResultSet results = null;
133
134         try {
135             String fullyQualifiedKey = keyspace + "." + tableName + "." + primaryKey;
136             ReturnType lockAcqResult = MusicCore.acquireLock(fullyQualifiedKey, lockId);
137             if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) {
138                 try {
139                     results = MusicDataStoreHandle.getDSHandle().executeQuorumConsistencyGet(queryBank.get(MusicUtil.SELECT));
140                 } catch (Exception e) {
141                     logger.error(EELFLoggerDelegate.applicationLogger, e);
142                     return new ReturnType(ResultType.FAILURE, e.getMessage());
143                 }
144                 if (results.all().isEmpty()) {
145                     MusicDataStoreHandle.getDSHandle().executePut(queryBank.get(MusicUtil.INSERT), "critical");
146                     return new ReturnType(ResultType.SUCCESS, "insert");
147                 } else {
148                     MusicDataStoreHandle.getDSHandle().executePut(queryBank.get(MusicUtil.UPDATE), "critical");
149                     return new ReturnType(ResultType.SUCCESS, "update");
150                 }
151             } else {
152                 return new ReturnType(ResultType.FAILURE,
153                         "Cannot perform operation since you are the not the lock holder");
154             }
155
156         } catch (Exception e) {
157             StringWriter sw = new StringWriter();
158             e.printStackTrace(new PrintWriter(sw));
159             String exceptionAsString = sw.toString();
160             logger.error(EELFLoggerDelegate.applicationLogger, e);
161             return new ReturnType(ResultType.FAILURE,
162                     "Exception thrown while doing the critical put, check sanctity of the row/conditions:\n"
163                             + exceptionAsString);
164         }
165
166     }
167
168     public static ReturnType update(UpdateDataObject dataObj)
169             throws MusicLockingException, MusicQueryException, MusicServiceException {
170
171         String key = dataObj.getKeyspace() + "." + dataObj.getTableName() + "." + dataObj.getPrimaryKeyValue();
172         String lockId = MusicCore.createLockReference(key);
173         long leasePeriod = MusicUtil.getDefaultLockLeasePeriod();
174         ReturnType lockAcqResult = MusicCore.acquireLockWithLease(key, lockId, leasePeriod);
175
176         try {
177
178             if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) {
179                 ReturnType criticalPutResult = updateAtomic(new UpdateDataObject().setLockId(lockId)
180                         .setKeyspace(dataObj.getKeyspace())
181                         .setTableName( dataObj.getTableName())
182                         .setPrimaryKey(dataObj.getPrimaryKey())
183                         .setPrimaryKeyValue(dataObj.getPrimaryKeyValue())
184                         .setQueryBank(dataObj.getQueryBank())
185                         .setPlanId(dataObj.getPlanId())
186                         .setCascadeColumnValues(dataObj.getCascadeColumnValues())
187                         .setCascadeColumnName(dataObj.getCascadeColumnName()));                     
188
189                 MusicCore.destroyLockRef(lockId);
190                 return criticalPutResult;
191             } else {
192                 MusicCore.destroyLockRef(lockId);
193                 return lockAcqResult;
194             }
195
196         } catch (Exception e) {
197             MusicCore.destroyLockRef(lockId);
198             logger.error(EELFLoggerDelegate.applicationLogger, e);
199             return new ReturnType(ResultType.FAILURE, e.getMessage());
200
201         }
202     }
203
204     public static ReturnType updateAtomic(UpdateDataObject dataObj) {
205         try {
206             String fullyQualifiedKey = dataObj.getKeyspace() + "." + dataObj.getTableName() + "." + dataObj.getPrimaryKeyValue();
207             ReturnType lockAcqResult = MusicCore.acquireLock(fullyQualifiedKey, dataObj.getLockId());
208
209             if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) {
210                 Row row  = MusicDataStoreHandle.getDSHandle().executeQuorumConsistencyGet(dataObj.getQueryBank().get(MusicUtil.SELECT)).one();
211                 
212                 if(row != null) {
213                     Map<String, String> updatedValues = cascadeColumnUpdateSpecific(row, dataObj.getCascadeColumnValues(), dataObj.getCascadeColumnName(), dataObj.getPlanId());
214                     JSONObject json = new JSONObject(updatedValues);
215                     PreparedQueryObject update = new PreparedQueryObject();
216                     String vector_ts = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
217                     update.appendQueryString("UPDATE " + dataObj.getKeyspace() + "." + dataObj.getTableName() + " SET " + dataObj.getCascadeColumnName() + "['" + dataObj.getPlanId()
218                             + "'] = ?, vector_ts = ? WHERE " + dataObj.getPrimaryKey() + " = ?");
219                     update.addValue(MusicUtil.convertToActualDataType(DataType.text(), json.toString()));
220                     update.addValue(MusicUtil.convertToActualDataType(DataType.text(), vector_ts));
221                     update.addValue(MusicUtil.convertToActualDataType(DataType.text(), dataObj.getPrimaryKeyValue()));
222                     try {
223                         MusicDataStoreHandle.getDSHandle().executePut(update, "critical");
224                     } catch (Exception ex) {
225                         logger.error(EELFLoggerDelegate.applicationLogger, ex);
226                         return new ReturnType(ResultType.FAILURE, ex.getMessage());
227                     }
228                 }else {
229                     return new ReturnType(ResultType.FAILURE,"Cannot find data related to key: "+dataObj.getPrimaryKey());
230                 }
231                 MusicDataStoreHandle.getDSHandle().executePut(dataObj.getQueryBank().get(MusicUtil.UPSERT), "critical");
232                 return new ReturnType(ResultType.SUCCESS, "update success");
233
234             } else {
235                 return new ReturnType(ResultType.FAILURE,
236                         "Cannot perform operation since you are the not the lock holder");
237             }
238
239         } catch (Exception e) {
240             StringWriter sw = new StringWriter();
241             e.printStackTrace(new PrintWriter(sw));
242             String exceptionAsString = sw.toString();
243             logger.error(EELFLoggerDelegate.applicationLogger, e);
244             return new ReturnType(ResultType.FAILURE,
245                     "Exception thrown while doing the critical put, check sanctity of the row/conditions:\n"
246                             + exceptionAsString);
247         }
248
249     }
250
251     @SuppressWarnings("unchecked")
252     public static Map<String, String> getValues(boolean isExists, Map<String, Object> casscadeColumnData,
253             Map<String, String> status) {
254
255         Map<String, String> returnMap = new HashMap<>();
256         Object key = casscadeColumnData.get("key");
257         String setStatus = "";
258         Map<String, String> value = (Map<String, String>) casscadeColumnData.get("value");
259
260         if (isExists)
261             setStatus = status.get("exists");
262         else
263             setStatus = status.get("nonexists");
264
265         value.put("status", setStatus);
266         JSONObject valueJson = new JSONObject(value);
267         returnMap.put(key.toString(), valueJson.toString());
268         return returnMap;
269
270     }
271     
272     public static PreparedQueryObject extractQuery(Map<String, Object> valuesMap, TableMetadata tableInfo, String tableName,
273             String keySpaceName,String primaryKeyName,String primaryKey,String casscadeColumn,Object casscadeColumnValues) throws Exception {
274
275         PreparedQueryObject queryObject = new PreparedQueryObject();
276         StringBuilder fieldsString = new StringBuilder("(vector_ts"+",");
277         StringBuilder valueString = new StringBuilder("(" + "?" + ",");
278         String vector = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
279         String localPrimaryKey;
280         queryObject.addValue(vector);
281         if(casscadeColumn!=null && casscadeColumnValues!=null) {
282             fieldsString.append(casscadeColumn).append(" ,");
283             valueString.append("?,");
284             queryObject.addValue(casscadeColumnValues);
285         }
286         
287         int counter = 0;
288         for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
289             
290             fieldsString.append(entry.getKey());
291             Object valueObj = entry.getValue();
292             if (primaryKeyName.equals(entry.getKey())) {
293                 localPrimaryKey = entry.getValue() + "";
294                 localPrimaryKey = localPrimaryKey.replace("'", "''");
295             }
296             DataType colType = null;
297             try {
298                 colType = tableInfo.getColumn(entry.getKey()).getType();
299             } catch(NullPointerException ex) {
300                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage() +" Invalid column name : "+entry.getKey(), 
301                     AppMessages.INCORRECTDATA  ,ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR, ex);
302             }
303
304             Object formattedValue = null;
305             try {
306                 formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
307             } catch (Exception e) {
308                 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), e);
309             }
310             
311             valueString.append("?");
312             queryObject.addValue(formattedValue);
313
314             
315             if (counter == valuesMap.size() - 1) {
316                 fieldsString.append(")");
317                 valueString.append(")");
318             } else {
319                 fieldsString.append(",");
320                 valueString.append(",");
321             }
322             counter = counter + 1;
323         }
324         queryObject.appendQueryString("INSERT INTO " + keySpaceName + "." + tableName + " "
325                 + fieldsString + " VALUES " + valueString);
326         return queryObject;
327     }
328     
329     public static Object getColValue(Row row, String colName, DataType colType) {
330         switch (colType.getName()) {
331         case VARCHAR:
332             return row.getString(colName);
333         case UUID:
334             return row.getUUID(colName);
335         case VARINT:
336             return row.getVarint(colName);
337         case BIGINT:
338             return row.getLong(colName);
339         case INT:
340             return row.getInt(colName);
341         case FLOAT:
342             return row.getFloat(colName);
343         case DOUBLE:
344             return row.getDouble(colName);
345         case BOOLEAN:
346             return row.getBool(colName);
347         case MAP:
348             return row.getMap(colName, String.class, String.class);
349         default:
350             return null;
351         }
352     }
353     
354     @SuppressWarnings("unchecked")
355     public static Map<String, String> cascadeColumnUpdateSpecific(Row row, Map<String, String> changeOfStatus,
356             String cascadeColumnName, String planId) {
357
358         ColumnDefinitions colInfo = row.getColumnDefinitions();
359         DataType colType = colInfo.getType(cascadeColumnName);
360         Object columnValue = getColValue(row, cascadeColumnName, colType);
361
362         Map<String, String> finalValues = new HashMap<>();
363         Map<String, String> values = (Map<String, String>) columnValue;
364         if (values != null && values.keySet().contains(planId)) {
365             String valueString = values.get(planId);
366             String tempValueString = valueString.replaceAll("\\{", "").replaceAll("\"", "").replaceAll("\\}", "");
367             String[] elements = tempValueString.split(",");
368             for (String str : elements) {
369                 String[] keyValue = str.split(":");
370                 if ((changeOfStatus.keySet().contains(keyValue[0].replaceAll("\\s", ""))))
371                 keyValue[1] = changeOfStatus.get(keyValue[0].replaceAll("\\s", ""));
372                 finalValues.put(keyValue[0], keyValue[1]);
373             }
374         }
375         return finalValues;
376
377     }
378
379 }