Code Coverage, Auth ID, Docker
[music.git] / src / main / java / org / onap / music / datastore / MusicDataStore.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
6  * ===================================================================
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  * 
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS,
15  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  * 
19  * ============LICENSE_END=============================================
20  * ====================================================================
21  */
22 package org.onap.music.datastore;
23
24 import java.net.InetAddress;
25 import java.net.NetworkInterface;
26 import java.net.SocketException;
27 import java.util.ArrayList;
28 import java.util.Enumeration;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.Map;
32 import org.onap.music.eelf.logging.EELFLoggerDelegate;
33 import org.onap.music.eelf.logging.format.AppMessages;
34 import org.onap.music.eelf.logging.format.ErrorSeverity;
35 import org.onap.music.eelf.logging.format.ErrorTypes;
36 import org.onap.music.exceptions.MusicQueryException;
37 import org.onap.music.exceptions.MusicServiceException;
38 import org.onap.music.main.MusicUtil;
39 import com.datastax.driver.core.Cluster;
40 import com.datastax.driver.core.ColumnDefinitions;
41 import com.datastax.driver.core.ColumnDefinitions.Definition;
42 import com.datastax.driver.core.ConsistencyLevel;
43 import com.datastax.driver.core.DataType;
44 import com.datastax.driver.core.KeyspaceMetadata;
45 import com.datastax.driver.core.Metadata;
46 import com.datastax.driver.core.PreparedStatement;
47 import com.datastax.driver.core.ResultSet;
48 import com.datastax.driver.core.Row;
49 import com.datastax.driver.core.Session;
50 import com.datastax.driver.core.TableMetadata;
51 import com.datastax.driver.core.exceptions.AlreadyExistsException;
52 import com.datastax.driver.core.exceptions.InvalidQueryException;
53 import com.datastax.driver.core.exceptions.NoHostAvailableException;
54
55 /**
56  * @author nelson24
57  *
58  */
59 public class MusicDataStore {
60
61     private Session session;
62     private Cluster cluster;
63
64
65
66     /**
67      * @param session
68      */
69     public void setSession(Session session) {
70         this.session = session;
71     }
72     
73     /**
74      * @param session
75      */
76     public Session getSession() {
77         return session;
78     }
79
80     /**
81      * @param cluster
82      */
83     public void setCluster(Cluster cluster) {
84         this.cluster = cluster;
85     }
86
87
88
89     private EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicDataStore.class);
90
91     /**
92      * 
93      */
94     public MusicDataStore() {
95         connectToCassaCluster();
96     }
97
98
99     /**
100      * @param cluster
101      * @param session
102      */
103     public MusicDataStore(Cluster cluster, Session session) {
104         this.session = session;
105         this.cluster = cluster;
106     }
107
108     /**
109      * 
110      * @param remoteIp
111      * @throws MusicServiceException
112      */
113     public MusicDataStore(String remoteIp) {
114         try {
115             connectToCassaCluster(remoteIp);
116         } catch (MusicServiceException e) {
117             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage());
118         }
119     }
120
121     /**
122      * 
123      * @return
124      */
125     private ArrayList<String> getAllPossibleLocalIps() {
126         ArrayList<String> allPossibleIps = new ArrayList<String>();
127         try {
128             Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
129             while (en.hasMoreElements()) {
130                 NetworkInterface ni = (NetworkInterface) en.nextElement();
131                 Enumeration<InetAddress> ee = ni.getInetAddresses();
132                 while (ee.hasMoreElements()) {
133                     InetAddress ia = (InetAddress) ee.nextElement();
134                     allPossibleIps.add(ia.getHostAddress());
135                 }
136             }
137         } catch (SocketException e) {
138             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(), AppMessages.CONNCECTIVITYERROR, ErrorSeverity.ERROR, ErrorTypes.CONNECTIONERROR);
139         }catch(Exception e) {
140                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(), ErrorSeverity.ERROR, ErrorTypes.GENERALSERVICEERROR);
141         }
142         return allPossibleIps;
143     }
144
145     /**
146      * This method iterates through all available IP addresses and connects to multiple cassandra
147      * clusters.
148      */
149     private void connectToCassaCluster() {
150         Iterator<String> it = getAllPossibleLocalIps().iterator();
151         String address = "localhost";
152         logger.info(EELFLoggerDelegate.applicationLogger,
153                         "Connecting to cassa cluster: Iterating through possible ips:"
154                                         + getAllPossibleLocalIps());
155         while (it.hasNext()) {
156             try {
157                 cluster = Cluster.builder().withPort(9042)
158                                 .withCredentials(MusicUtil.getCassName(), MusicUtil.getCassPwd())
159                                 .addContactPoint(address).build();
160                 Metadata metadata = cluster.getMetadata();
161                 logger.info(EELFLoggerDelegate.applicationLogger, "Connected to cassa cluster "
162                                 + metadata.getClusterName() + " at " + address);
163                 session = cluster.connect();
164
165                 break;
166             } catch (NoHostAvailableException e) {
167                 address = it.next();
168                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.HOSTUNAVAILABLE, ErrorSeverity.ERROR, ErrorTypes.CONNECTIONERROR);
169             }
170         }
171     }
172
173     /**
174      * 
175      */
176     public void close() {
177         session.close();
178     }
179
180     /**
181      * This method connects to cassandra cluster on specific address.
182      * 
183      * @param address
184      */
185     private void connectToCassaCluster(String address) throws MusicServiceException {
186         cluster = Cluster.builder().withPort(9042)
187                         .withCredentials(MusicUtil.getCassName(), MusicUtil.getCassPwd())
188                         .addContactPoint(address).build();
189         Metadata metadata = cluster.getMetadata();
190         logger.info(EELFLoggerDelegate.applicationLogger, "Connected to cassa cluster "
191                         + metadata.getClusterName() + " at " + address);
192         try {
193             session = cluster.connect();
194         } catch (Exception ex) {
195             logger.error(EELFLoggerDelegate.errorLogger, ex.getMessage(),AppMessages.CASSANDRACONNECTIVITY, ErrorSeverity.ERROR, ErrorTypes.SERVICEUNAVAILABLE);
196             throw new MusicServiceException(
197                             "Error while connecting to Cassandra cluster.. " + ex.getMessage());
198         }
199     }
200
201     /**
202      * 
203      * @param keyspace
204      * @param tableName
205      * @param columnName
206      * @return DataType
207      */
208     public DataType returnColumnDataType(String keyspace, String tableName, String columnName) {
209         KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(keyspace);
210         TableMetadata table = ks.getTable(tableName);
211         return table.getColumn(columnName).getType();
212
213     }
214
215     /**
216      * 
217      * @param keyspace
218      * @param tableName
219      * @return TableMetadata
220      */
221     public TableMetadata returnColumnMetadata(String keyspace, String tableName) {
222         KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(keyspace);
223         return ks.getTable(tableName);
224     }
225
226
227     /**
228      * Utility function to return the Java specific object type.
229      * 
230      * @param row
231      * @param colName
232      * @param colType
233      * @return
234      */
235     public Object getColValue(Row row, String colName, DataType colType) {
236
237         switch (colType.getName()) {
238             case VARCHAR:
239                 return row.getString(colName);
240             case UUID:
241                 return row.getUUID(colName);
242             case VARINT:
243                 return row.getVarint(colName);
244             case BIGINT:
245                 return row.getLong(colName);
246             case INT:
247                 return row.getInt(colName);
248             case FLOAT:
249                 return row.getFloat(colName);
250             case DOUBLE:
251                 return row.getDouble(colName);
252             case BOOLEAN:
253                 return row.getBool(colName);
254             case MAP:
255                 return row.getMap(colName, String.class, String.class);
256             default:
257                 return null;
258         }
259     }
260
261     public boolean doesRowSatisfyCondition(Row row, Map<String, Object> condition) throws Exception {
262         ColumnDefinitions colInfo = row.getColumnDefinitions();
263
264         for (Map.Entry<String, Object> entry : condition.entrySet()) {
265             String colName = entry.getKey();
266             DataType colType = colInfo.getType(colName);
267             Object columnValue = getColValue(row, colName, colType);
268             Object conditionValue = MusicUtil.convertToActualDataType(colType, entry.getValue());
269             if (columnValue.equals(conditionValue) == false)
270                 return false;
271         }
272         return true;
273     }
274
275     /**
276      * Utility function to store ResultSet values in to a MAP for output.
277      * 
278      * @param results
279      * @return MAP
280      */
281     public Map<String, HashMap<String, Object>> marshalData(ResultSet results) {
282         Map<String, HashMap<String, Object>> resultMap =
283                         new HashMap<String, HashMap<String, Object>>();
284         int counter = 0;
285         for (Row row : results) {
286             ColumnDefinitions colInfo = row.getColumnDefinitions();
287             HashMap<String, Object> resultOutput = new HashMap<String, Object>();
288             for (Definition definition : colInfo) {
289                 if (!definition.getName().equals("vector_ts"))
290                     resultOutput.put(definition.getName(),
291                                     getColValue(row, definition.getName(), definition.getType()));
292             }
293             resultMap.put("row " + counter, resultOutput);
294             counter++;
295         }
296         return resultMap;
297     }
298
299
300     // Prepared Statements 1802 additions
301     /**
302      * This Method performs DDL and DML operations on Cassandra using specified consistency level
303      * 
304      * @param queryObject Object containing cassandra prepared query and values.
305      * @param consistency Specify consistency level for data synchronization across cassandra
306      *        replicas
307      * @return Boolean Indicates operation success or failure
308      * @throws MusicServiceException
309      * @throws MusicQueryException
310      */
311     public boolean executePut(PreparedQueryObject queryObject, String consistency)
312                     throws MusicServiceException, MusicQueryException {
313
314         boolean result = false;
315
316         if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) {
317                 logger.error(EELFLoggerDelegate.errorLogger, queryObject.getQuery(),AppMessages.QUERYERROR, ErrorSeverity.ERROR, ErrorTypes.QUERYERROR);
318             throw new MusicQueryException("Ill formed queryObject for the request = " + "["
319                             + queryObject.getQuery() + "]");
320         }
321         logger.info(EELFLoggerDelegate.applicationLogger,
322                         "In preprared Execute Put: the actual insert query:"
323                                         + queryObject.getQuery() + "; the values"
324                                         + queryObject.getValues());
325         PreparedStatement preparedInsert = null;
326         try {
327                 preparedInsert = session.prepare(queryObject.getQuery());
328         } catch(InvalidQueryException iqe) {
329                 logger.error(EELFLoggerDelegate.errorLogger, iqe.getMessage());
330                 throw new MusicQueryException(iqe.getMessage());
331         }
332         
333         try {
334             if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) {
335                 logger.info(EELFLoggerDelegate.applicationLogger, "Executing critical put query");
336                 preparedInsert.setConsistencyLevel(ConsistencyLevel.QUORUM);
337             } else if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) {
338                 logger.info(EELFLoggerDelegate.applicationLogger, "Executing simple put query");
339                 preparedInsert.setConsistencyLevel(ConsistencyLevel.ONE);
340             }
341
342             ResultSet rs = session.execute(preparedInsert.bind(queryObject.getValues().toArray()));
343             result = rs.wasApplied();
344
345         }
346         catch (AlreadyExistsException ae) {
347             logger.error(EELFLoggerDelegate.errorLogger, ae.getMessage(),AppMessages.SESSIONFAILED+ " [" + queryObject.getQuery() + "]", ErrorSeverity.ERROR, ErrorTypes.QUERYERROR);
348                 throw new MusicServiceException(ae.getMessage());
349         }
350         catch (Exception e) {
351                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.SESSIONFAILED+ " [" + queryObject.getQuery() + "]", ErrorSeverity.ERROR, ErrorTypes.QUERYERROR);
352                 throw new MusicQueryException("Executing Session Failure for Request = " + "["
353                             + queryObject.getQuery() + "]" + " Reason = " + e.getMessage());
354         }
355
356
357         return result;
358     }
359
360     /**
361      * This method performs DDL operations on Cassandra using consistency level ONE.
362      * 
363      * @param queryObject Object containing cassandra prepared query and values.
364      * @return ResultSet
365      * @throws MusicServiceException
366      * @throws MusicQueryException
367      */
368     public ResultSet executeEventualGet(PreparedQueryObject queryObject)
369                     throws MusicServiceException, MusicQueryException {
370
371         if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) {
372                 logger.error(EELFLoggerDelegate.errorLogger, "",AppMessages.QUERYERROR+ " [" + queryObject.getQuery() + "]", ErrorSeverity.ERROR, ErrorTypes.QUERYERROR);
373                 throw new MusicQueryException("Ill formed queryObject for the request = " + "["
374                             + queryObject.getQuery() + "]");
375         }
376         logger.info(EELFLoggerDelegate.applicationLogger,
377                         "Executing Eventual  get query:" + queryObject.getQuery());
378        
379         ResultSet results = null;
380         try {
381                  PreparedStatement preparedEventualGet = session.prepare(queryObject.getQuery());
382              preparedEventualGet.setConsistencyLevel(ConsistencyLevel.ONE);
383              results = session.execute(preparedEventualGet.bind(queryObject.getValues().toArray()));
384
385         } catch (Exception ex) {
386                 logger.error(EELFLoggerDelegate.errorLogger, ex.getMessage(),AppMessages.UNKNOWNERROR+ "[" + queryObject.getQuery() + "]", ErrorSeverity.ERROR, ErrorTypes.QUERYERROR);
387                 throw new MusicServiceException(ex.getMessage());
388         }
389         return results;
390     }
391
392     /**
393      * 
394      * This method performs DDL operation on Cassandra using consistency level QUORUM.
395      * 
396      * @param queryObject Object containing cassandra prepared query and values.
397      * @return ResultSet
398      * @throws MusicServiceException
399      * @throws MusicQueryException
400      */
401     public ResultSet executeCriticalGet(PreparedQueryObject queryObject)
402                     throws MusicServiceException, MusicQueryException {
403         if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) {
404                 logger.error(EELFLoggerDelegate.errorLogger, "",AppMessages.QUERYERROR+ " [" + queryObject.getQuery() + "]", ErrorSeverity.ERROR, ErrorTypes.QUERYERROR);
405             throw new MusicQueryException("Error processing Prepared Query Object for the request = " + "["
406                             + queryObject.getQuery() + "]");
407         }
408         logger.info(EELFLoggerDelegate.applicationLogger,
409                         "Executing Critical get query:" + queryObject.getQuery());
410         PreparedStatement preparedEventualGet = session.prepare(queryObject.getQuery());
411         preparedEventualGet.setConsistencyLevel(ConsistencyLevel.QUORUM);
412         ResultSet results = null;
413         try {
414             results = session.execute(preparedEventualGet.bind(queryObject.getValues().toArray()));
415         } catch (Exception ex) {
416                 logger.error(EELFLoggerDelegate.errorLogger, ex.getMessage(),AppMessages.UNKNOWNERROR+ "[" + queryObject.getQuery() + "]", ErrorSeverity.ERROR, ErrorTypes.QUERYERROR);
417                 throw new MusicServiceException(ex.getMessage());
418         }
419         return results;
420
421     }
422
423 }