Changes Listed below:
[music.git] / 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(Map<String, PreparedQueryObject> queryBank, String keyspace, String tableName,
169                         String primaryKey, String primaryKeyValue, String planId, String cascadeColumnName,
170                         Map<String, String> cascadeColumnValues)
171                         throws MusicLockingException, MusicQueryException, MusicServiceException {
172
173         String key = keyspace + "." + tableName + "." + primaryKeyValue;
174         String lockId = MusicCore.createLockReference(key);
175         long leasePeriod = MusicUtil.getDefaultLockLeasePeriod();
176         ReturnType lockAcqResult = MusicCore.acquireLockWithLease(key, lockId, leasePeriod);
177
178         try {
179
180             if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) {
181                 ReturnType criticalPutResult = updateAtomic(lockId, keyspace, tableName, primaryKey,primaryKeyValue, queryBank,planId,cascadeColumnValues,cascadeColumnName);
182                 MusicCore.destroyLockRef(lockId);
183                 return criticalPutResult;
184             } else {
185                 MusicCore.destroyLockRef(lockId);
186                 return lockAcqResult;
187             }
188
189         } catch (Exception e) {
190             MusicCore.destroyLockRef(lockId);
191             logger.error(EELFLoggerDelegate.applicationLogger, e);
192             return new ReturnType(ResultType.FAILURE, e.getMessage());
193
194         }
195     }
196
197     public static ReturnType updateAtomic(String lockId, String keyspace, String tableName, String primaryKey,String primaryKeyValue,
198             Map<String,PreparedQueryObject> queryBank,String planId,Map<String,String> cascadeColumnValues,String casscadeColumnName) {
199         try {
200             String fullyQualifiedKey = keyspace + "." + tableName + "." + primaryKeyValue;
201             ReturnType lockAcqResult = MusicCore.acquireLock(fullyQualifiedKey, lockId);
202
203             if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) {
204                 Row row  = MusicDataStoreHandle.getDSHandle().executeQuorumConsistencyGet(queryBank.get(MusicUtil.SELECT)).one();
205                 
206                 if(row != null) {
207                     Map<String, String> updatedValues = cascadeColumnUpdateSpecific(row, cascadeColumnValues, casscadeColumnName, planId);
208                     JSONObject json = new JSONObject(updatedValues);
209                     PreparedQueryObject update = new PreparedQueryObject();
210                     String vector_ts = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
211                     update.appendQueryString("UPDATE " + keyspace + "." + tableName + " SET " + casscadeColumnName + "['" + planId
212                             + "'] = ?, vector_ts = ? WHERE " + primaryKey + " = ?");
213                     update.addValue(MusicUtil.convertToActualDataType(DataType.text(), json.toString()));
214                     update.addValue(MusicUtil.convertToActualDataType(DataType.text(), vector_ts));
215                     update.addValue(MusicUtil.convertToActualDataType(DataType.text(), primaryKeyValue));
216                     try {
217                         MusicDataStoreHandle.getDSHandle().executePut(update, "critical");
218                     } catch (Exception ex) {
219                         logger.error(EELFLoggerDelegate.applicationLogger, ex);
220                         return new ReturnType(ResultType.FAILURE, ex.getMessage());
221                     }
222                 }else {
223                     return new ReturnType(ResultType.FAILURE,"Cannot find data related to key: "+primaryKey);
224                 }
225                 MusicDataStoreHandle.getDSHandle().executePut(queryBank.get(MusicUtil.UPSERT), "critical");
226                 return new ReturnType(ResultType.SUCCESS, "update success");
227
228             } else {
229                 return new ReturnType(ResultType.FAILURE,
230                         "Cannot perform operation since you are the not the lock holder");
231             }
232
233         } catch (Exception e) {
234             StringWriter sw = new StringWriter();
235             e.printStackTrace(new PrintWriter(sw));
236             String exceptionAsString = sw.toString();
237             logger.error(EELFLoggerDelegate.applicationLogger, e);
238             return new ReturnType(ResultType.FAILURE,
239                     "Exception thrown while doing the critical put, check sanctity of the row/conditions:\n"
240                             + exceptionAsString);
241         }
242
243     }
244
245     @SuppressWarnings("unchecked")
246     public static Map<String, String> getValues(boolean isExists, Map<String, Object> casscadeColumnData,
247             Map<String, String> status) {
248
249         Map<String, String> returnMap = new HashMap<>();
250         Object key = casscadeColumnData.get("key");
251         String setStatus = "";
252         Map<String, String> value = (Map<String, String>) casscadeColumnData.get("value");
253
254         if (isExists)
255             setStatus = status.get("exists");
256         else
257             setStatus = status.get("nonexists");
258
259         value.put("status", setStatus);
260         JSONObject valueJson = new JSONObject(value);
261         returnMap.put(key.toString(), valueJson.toString());
262         return returnMap;
263
264     }
265     
266     public static PreparedQueryObject extractQuery(Map<String, Object> valuesMap, TableMetadata tableInfo, String tableName,
267             String keySpaceName,String primaryKeyName,String primaryKey,String casscadeColumn,Object casscadeColumnValues) throws Exception {
268
269         PreparedQueryObject queryObject = new PreparedQueryObject();
270         StringBuilder fieldsString = new StringBuilder("(vector_ts"+",");
271         StringBuilder valueString = new StringBuilder("(" + "?" + ",");
272         String vector = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis());
273         String localPrimaryKey;
274         queryObject.addValue(vector);
275         if(casscadeColumn!=null && casscadeColumnValues!=null) {
276             fieldsString.append(casscadeColumn).append(" ,");
277             valueString.append("?,");
278             queryObject.addValue(casscadeColumnValues);
279         }
280         
281         int counter = 0;
282         for (Map.Entry<String, Object> entry : valuesMap.entrySet()) {
283             
284             fieldsString.append(entry.getKey());
285             Object valueObj = entry.getValue();
286             if (primaryKeyName.equals(entry.getKey())) {
287                 localPrimaryKey = entry.getValue() + "";
288                 localPrimaryKey = localPrimaryKey.replace("'", "''");
289             }
290             DataType colType = null;
291             try {
292                 colType = tableInfo.getColumn(entry.getKey()).getType();
293             } catch(NullPointerException ex) {
294                 logger.error(EELFLoggerDelegate.errorLogger,ex.getMessage() +" Invalid column name : "+entry.getKey(), 
295                     AppMessages.INCORRECTDATA  ,ErrorSeverity.CRITICAL, ErrorTypes.DATAERROR, ex);
296             }
297
298             Object formattedValue = null;
299             try {
300                 formattedValue = MusicUtil.convertToActualDataType(colType, valueObj);
301             } catch (Exception e) {
302                 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), e);
303             }
304             
305             valueString.append("?");
306             queryObject.addValue(formattedValue);
307
308             
309             if (counter == valuesMap.size() - 1) {
310                 fieldsString.append(")");
311                 valueString.append(")");
312             } else {
313                 fieldsString.append(",");
314                 valueString.append(",");
315             }
316             counter = counter + 1;
317         }
318         queryObject.appendQueryString("INSERT INTO " + keySpaceName + "." + tableName + " "
319                 + fieldsString + " VALUES " + valueString);
320         return queryObject;
321     }
322     
323     public static Object getColValue(Row row, String colName, DataType colType) {
324         switch (colType.getName()) {
325         case VARCHAR:
326             return row.getString(colName);
327         case UUID:
328             return row.getUUID(colName);
329         case VARINT:
330             return row.getVarint(colName);
331         case BIGINT:
332             return row.getLong(colName);
333         case INT:
334             return row.getInt(colName);
335         case FLOAT:
336             return row.getFloat(colName);
337         case DOUBLE:
338             return row.getDouble(colName);
339         case BOOLEAN:
340             return row.getBool(colName);
341         case MAP:
342             return row.getMap(colName, String.class, String.class);
343         default:
344             return null;
345         }
346     }
347     
348     @SuppressWarnings("unchecked")
349     public static Map<String, String> cascadeColumnUpdateSpecific(Row row, Map<String, String> changeOfStatus,
350             String cascadeColumnName, String planId) {
351
352         ColumnDefinitions colInfo = row.getColumnDefinitions();
353         DataType colType = colInfo.getType(cascadeColumnName);
354         Object columnValue = getColValue(row, cascadeColumnName, colType);
355
356         Map<String, String> finalValues = new HashMap<>();
357         Map<String, String> values = (Map<String, String>) columnValue;
358         if (values != null && values.keySet().contains(planId)) {
359             String valueString = values.get(planId);
360             String tempValueString = valueString.replaceAll("\\{", "").replaceAll("\"", "").replaceAll("\\}", "");
361             String[] elements = tempValueString.split(",");
362             for (String str : elements) {
363                 String[] keyValue = str.split(":");
364                 if ((changeOfStatus.keySet().contains(keyValue[0].replaceAll("\\s", ""))))
365                 keyValue[1] = changeOfStatus.get(keyValue[0].replaceAll("\\s", ""));
366                 finalValues.put(keyValue[0], keyValue[1]);
367             }
368         }
369         return finalValues;
370
371     }
372
373 }