2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright (c) 2017 AT&T Intellectual Property
6 * ===================================================================
7 * Modifications Copyright (c) 2018 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
14 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 * ============LICENSE_END=============================================
23 * ====================================================================
26 package org.onap.music.main;
28 import com.datastax.driver.core.ColumnDefinitions;
29 import com.datastax.driver.core.ColumnDefinitions.Definition;
30 import com.datastax.driver.core.ResultSet;
31 import com.datastax.driver.core.Row;
33 import java.io.FileNotFoundException;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.math.BigInteger;
37 import java.nio.ByteBuffer;
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.List;
42 import java.util.Properties;
43 import java.util.Scanner;
44 import java.util.StringTokenizer;
45 import java.util.UUID;
46 import java.util.concurrent.ConcurrentHashMap;
47 import java.util.concurrent.ConcurrentMap;
49 import javax.ws.rs.core.Response;
50 import javax.ws.rs.core.Response.ResponseBuilder;
51 import org.onap.music.datastore.MusicDataStoreHandle;
52 import org.onap.music.datastore.PreparedQueryObject;
53 import org.onap.music.eelf.logging.EELFLoggerDelegate;
54 import org.onap.music.eelf.logging.format.AppMessages;
55 import org.onap.music.eelf.logging.format.ErrorSeverity;
56 import org.onap.music.eelf.logging.format.ErrorTypes;
57 import org.onap.music.exceptions.MusicQueryException;
58 import org.onap.music.exceptions.MusicServiceException;
59 import org.onap.music.service.MusicCoreService;
60 import org.onap.music.service.impl.MusicCassaCore;
62 import com.datastax.driver.core.ConsistencyLevel;
63 import com.datastax.driver.core.DataType;
64 import com.sun.jersey.core.util.Base64;
69 * Properties This will take Properties and load them into MusicUtil.
70 * This is a hack for now. Eventually it would bebest to do this in
74 public class MusicUtil {
75 private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicUtil.class);
77 public static final String ATOMIC = "atomic";
78 public static final String EVENTUAL = "eventual";
79 public static final String CRITICAL = "critical";
80 public static final String EVENTUAL_NB = "eventual_nb";
81 public static final String ALL = "all";
82 public static final String QUORUM = "quorum";
83 public static final String ONE = "one";
84 public static final String ATOMICDELETELOCK = "atomic_delete_lock";
85 public static final String DEFAULTKEYSPACENAME = "TBD";
86 private static final String XLATESTVERSION = "X-latestVersion";
87 private static final String XMINORVERSION = "X-minorVersion";
88 private static final String XPATCHVERSION = "X-patchVersion";
89 public static final String SELECT = "select";
90 public static final String INSERT = "insert";
91 public static final String UPDATE = "update";
92 public static final String UPSERT = "upsert";
93 public static final String USERID = "userId";
94 public static final String PASSWORD = "password";
95 public static final String CASSANDRA = "cassandra";
97 public static final String AUTHORIZATION = "Authorization";
99 private static final String LOCALHOST = "localhost";
100 private static final String PROPERTIES_FILE = "/opt/app/music/etc/music.properties";
102 private static int myId = 0;
103 private static ArrayList<String> allIds = new ArrayList<>();
104 private static String publicIp = "";
105 private static ArrayList<String> allPublicIps = new ArrayList<>();
106 private static String myZkHost = LOCALHOST;
107 private static String myCassaHost = LOCALHOST;
108 private static String defaultMusicIp = LOCALHOST;
109 private static int cassandraPort = 9042;
110 private static int notifytimeout = 30000;
111 private static int notifyinterval = 5000;
112 private static int cacheObjectMaxLife = -1;
113 private static String lockUsing = MusicUtil.CASSANDRA;
114 private static boolean isCadi = false;
115 private static boolean isKeyspaceActive = false;
117 private static boolean debug = true;
118 private static String version = "0.0.0";
119 private static String build = "";
120 private static String musicRestIp = LOCALHOST;
121 private static String musicPropertiesFilePath = PROPERTIES_FILE;
122 private static long defaultLockLeasePeriod = 6000;
123 private static int retryCount = 3;
124 private static final String[] propKeys = new String[] { "cassandra.host", "music.ip", "debug",
125 "version", "music.rest.ip", "music.properties", "lock.lease.period", "id", "all.ids",
126 "public.ip","all.pubic.ips", "cassandra.user", "cassandra.password", "aaf.endpoint.url",
127 "admin.username","admin.password","aaf.admin.url","music.namespace","admin.aaf.role",
128 "cassandra.port","lock.using","retry.count"};
129 private static final String[] cosistencyLevel = new String[] {
130 "ALL","EACH_QUORUM","QUORUM","LOCAL_QUORUM","ONE","TWO",
131 "THREE","LOCAL_ONE","ANY","SERIAL","LOCAL_SERIAL"};
132 private static final Map<String,ConsistencyLevel> consistencyName = new HashMap<>();
135 consistencyName.put("ONE",ConsistencyLevel.ONE);
136 consistencyName.put("TWO",ConsistencyLevel.TWO);
137 consistencyName.put("THREE",ConsistencyLevel.THREE);
138 consistencyName.put("SERIAL",ConsistencyLevel.SERIAL);
139 consistencyName.put("ALL",ConsistencyLevel.ALL);
140 consistencyName.put("EACH_QUORUM",ConsistencyLevel.EACH_QUORUM);
141 consistencyName.put("QUORUM",ConsistencyLevel.QUORUM);
142 consistencyName.put("LOCAL_QUORUM",ConsistencyLevel.LOCAL_QUORUM);
143 consistencyName.put("LOCAL_ONE",ConsistencyLevel.LOCAL_ONE);
144 consistencyName.put("LOCAL_SERIAL",ConsistencyLevel.LOCAL_SERIAL);
147 private static String cassName = "cassandra";
148 private static String cassPwd;
149 private static String aafEndpointUrl = null;
150 public static ConcurrentMap<String, Long> zkNodeMap = new ConcurrentHashMap<>();
151 private static String adminId = "username";
152 private static String adminPass= "password";
153 private static String aafAdminUrl= null;
154 private static String musicNamespace= "org.onap.music.api";
155 private static String adminAafRole= "org.onap.music.api.admin_api";
157 public static final long MusicEternityEpochMillis = 1533081600000L; // Wednesday, August 1, 2018 12:00:00 AM
159 public static final long MaxLockReferenceTimePart = 1000000000000L; // millis after eternity (eq sometime in 2050)
161 public static final long MaxCriticalSectionDurationMillis = 1L * 24 * 60 * 60 * 1000; // 1 day
164 public static String getLockUsing() {
169 public static void setLockUsing(String lockUsing) {
170 MusicUtil.lockUsing = lockUsing;
173 public static String getAafAdminUrl() {
178 public static void setAafAdminUrl(String aafAdminUrl) {
179 MusicUtil.aafAdminUrl = aafAdminUrl;
183 public static String getMusicNamespace() {
184 return musicNamespace;
188 public static void setMusicNamespace(String musicNamespace) {
189 MusicUtil.musicNamespace = musicNamespace;
193 public static String getAdminAafRole() {
198 public static void setAdminAafRole(String adminAafRole) {
199 MusicUtil.adminAafRole = adminAafRole;
204 public static String getAdminId() {
209 public static void setAdminId(String adminId) {
210 MusicUtil.adminId = adminId;
214 public static String getAdminPass() {
218 public static void setAdminPass(String adminPass) {
219 MusicUtil.adminPass = adminPass;
222 private MusicUtil() {
223 throw new IllegalStateException("Utility Class");
227 * @return cassandra port
229 public static int getCassandraPort() {
230 return cassandraPort;
235 * @param cassandraPort
237 public static void setCassandraPort(int cassandraPort) {
238 MusicUtil.cassandraPort = cassandraPort;
241 * @return the cassName
243 public static String getCassName() {
248 * @return the cassPwd
250 public static String getCassPwd() {
255 * @return the aafEndpointUrl
257 public static String getAafEndpointUrl() {
258 return aafEndpointUrl;
263 * @param aafEndpointUrl
265 public static void setAafEndpointUrl(String aafEndpointUrl) {
266 MusicUtil.aafEndpointUrl = aafEndpointUrl;
273 public static int getMyId() {
281 public static void setMyId(int myId) {
282 MusicUtil.myId = myId;
289 public static List<String> getAllIds() {
297 public static void setAllIds(List<String> allIds) {
298 MusicUtil.allIds = (ArrayList<String>) allIds;
305 public static String getPublicIp() {
313 public static void setPublicIp(String publicIp) {
314 MusicUtil.publicIp = publicIp;
321 public static List<String> getAllPublicIps() {
327 * @param allPublicIps
329 public static void setAllPublicIps(List<String> allPublicIps) {
330 MusicUtil.allPublicIps = (ArrayList<String>) allPublicIps;
334 * Returns An array of property names that should be in the Properties
339 public static String[] getPropkeys() {
344 * Get MusicRestIp - default = localhost property file value - music.rest.ip
348 public static String getMusicRestIp() {
357 public static void setMusicRestIp(String musicRestIp) {
358 MusicUtil.musicRestIp = musicRestIp;
362 * Get MusicPropertiesFilePath - Default = /opt/music/music.properties
363 * property file value - music.properties
367 public static String getMusicPropertiesFilePath() {
368 return musicPropertiesFilePath;
372 * Set MusicPropertiesFilePath
374 * @param musicPropertiesFilePath
376 public static void setMusicPropertiesFilePath(String musicPropertiesFilePath) {
377 MusicUtil.musicPropertiesFilePath = musicPropertiesFilePath;
381 * Get DefaultLockLeasePeriod - Default = 6000 property file value -
386 public static long getDefaultLockLeasePeriod() {
387 return defaultLockLeasePeriod;
391 * Set DefaultLockLeasePeriod
393 * @param defaultLockLeasePeriod
395 public static void setDefaultLockLeasePeriod(long defaultLockLeasePeriod) {
396 MusicUtil.defaultLockLeasePeriod = defaultLockLeasePeriod;
404 public static void setDebug(boolean debug) {
405 MusicUtil.debug = debug;
409 * Is Debug - Default = true property file value - debug
413 public static boolean isDebug() {
422 public static void setVersion(String version) {
423 MusicUtil.version = version;
427 * Return the version property file value - version.
431 public static String getVersion() {
436 * Set the build of project which is a combination of the
437 * version and the date.
439 * @param build - version-date.
441 public static void setBuild(String build) {
442 MusicUtil.build = build;
446 * Return the build version-date.
448 public static String getBuild() {
453 * Get MyCassHost - Cassandra Hostname - Default = localhost property file
454 * value - cassandra.host
458 public static String getMyCassaHost() {
463 * Set MyCassHost - Cassandra Hostname
465 * @param myCassaHost .
467 public static void setMyCassaHost(String myCassaHost) {
468 MusicUtil.myCassaHost = myCassaHost;
472 * Get DefaultMusicIp - Default = localhost property file value - music.ip
476 public static String getDefaultMusicIp() {
477 return defaultMusicIp;
483 * @param defaultMusicIp .
485 public static void setDefaultMusicIp(String defaultMusicIp) {
486 MusicUtil.defaultMusicIp = defaultMusicIp;
490 * Gey default retry count
493 public static int getRetryCount() {
499 * @param retryCount .
501 public static void setRetryCount(int retryCount) {
502 MusicUtil.retryCount = retryCount;
507 * This is used to turn keyspace creation api on/off.
510 public static void setKeyspaceActive(Boolean keyspaceActive) {
511 MusicUtil.isKeyspaceActive = keyspaceActive;
515 * This is used to turn keyspace creation api on/off.
516 * @return boolean isKeyspaceActive
518 public static boolean isKeyspaceActive() {
519 return isKeyspaceActive;
526 public static String getTestType() {
527 String testType = "";
529 Scanner fileScanner = new Scanner(new File(""));
530 testType = fileScanner.next();// ignore the my id line
531 @SuppressWarnings("unused")
532 String batchSize = fileScanner.next();// ignore the my public ip line
534 } catch (FileNotFoundException e) {
535 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(), e);
545 public static void sleep(long time) {
548 } catch (InterruptedException e) {
549 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(), e);
550 Thread.currentThread().interrupt();
555 * Utility function to check if the query object is valid.
561 public static boolean isValidQueryObject(boolean withparams, PreparedQueryObject queryObject) {
563 int noOfValues = queryObject.getValues().size();
565 char[] temp = queryObject.getQuery().toCharArray();
566 for (int i = 0; i < temp.length; i++) {
570 return (noOfValues == noOfParams);
572 return !queryObject.getQuery().isEmpty();
577 public static void setCassName(String cassName) {
578 MusicUtil.cassName = cassName;
581 public static void setCassPwd(String cassPwd) {
582 MusicUtil.cassPwd = cassPwd;
585 @SuppressWarnings("unchecked")
586 public static String convertToCQLDataType(DataType type, Object valueObj) throws Exception {
589 switch (type.getName()) {
591 value = valueObj + "";
595 String valueString = valueObj + "";
596 valueString = valueString.replace("'", "''");
597 value = "'" + valueString + "'";
600 Map<String, Object> otMap = (Map<String, Object>) valueObj;
601 value = "{" + jsonMaptoSqlString(otMap, ",") + "}";
605 value = valueObj + "";
616 * @throws MusicTypeConversionException
619 @SuppressWarnings("unchecked")
620 public static Object convertToActualDataType(DataType colType, Object valueObj) throws Exception {
621 String valueObjString = valueObj + "";
622 switch (colType.getName()) {
624 return UUID.fromString(valueObjString);
626 return BigInteger.valueOf(Long.parseLong(valueObjString));
628 return Long.parseLong(valueObjString);
630 return Integer.parseInt(valueObjString);
632 return Float.parseFloat(valueObjString);
634 return Double.parseDouble(valueObjString);
636 return Boolean.parseBoolean(valueObjString);
638 return (Map<String, Object>) valueObj;
640 return (List<Object>)valueObj;
644 return valueObjString;
648 public static ByteBuffer convertToActualDataType(DataType colType, byte[] valueObj) {
649 ByteBuffer buffer = ByteBuffer.wrap(valueObj);
655 * Utility function to parse json map into sql like string
658 * @param lineDelimiter
662 public static String jsonMaptoSqlString(Map<String, Object> jMap, String lineDelimiter) throws Exception{
663 StringBuilder sqlString = new StringBuilder();
665 for (Map.Entry<String, Object> entry : jMap.entrySet()) {
666 Object ot = entry.getValue();
667 String value = ot + "";
668 if (ot instanceof String) {
669 value = "'" + value.replace("'", "''") + "'";
671 sqlString.append("'" + entry.getKey() + "':" + value);
672 if (counter != jMap.size() - 1)
673 sqlString.append(lineDelimiter);
674 counter = counter + 1;
676 return sqlString.toString();
679 @SuppressWarnings("unused")
680 public static String buildVersion(String major, String minor, String patch) {
682 major += "." + minor;
684 major += "." + patch;
691 * Currently this will build a header with X-latestVersion, X-minorVersion and X-pathcVersion
692 * X-latestVerstion will be equal to the latest full version.
693 * X-minorVersion - will be equal to the latest minor version.
694 * X-pathVersion - will be equal to the latest patch version.
695 * Future plans will change this.
702 public static ResponseBuilder buildVersionResponse(String major, String minor, String patch) {
703 ResponseBuilder response = Response.noContent();
704 String versionIn = buildVersion(major,minor,patch);
705 String version = MusicUtil.getVersion();
706 String[] verArray = version.split("\\.",3);
707 if ( minor != null ) {
708 response.header(XMINORVERSION,minor);
710 response.header(XMINORVERSION,verArray[1]);
712 if ( patch != null ) {
713 response.header(XPATCHVERSION,patch);
715 response.header(XPATCHVERSION,verArray[2]);
717 response.header(XLATESTVERSION,version);
718 logger.info(EELFLoggerDelegate.applicationLogger,"Version In:" + versionIn);
723 public static Map<String,String> extractBasicAuthentication(String authorization){
724 Map<String,String> authValues = new HashMap<>();
725 if(authorization == null) {
726 authValues.put("ERROR", "Authorization cannot be null");
729 authorization = authorization.replaceFirst("Basic", "");
730 String decoded = Base64.base64Decode(authorization);
731 StringTokenizer token = new StringTokenizer(decoded, ":");
732 authValues.put(MusicUtil.USERID, token.nextToken());
733 authValues.put(MusicUtil.PASSWORD,token.nextToken());
738 public static boolean isValidConsistency(String consistency) {
739 for (String string : cosistencyLevel) {
740 if (string.equalsIgnoreCase(consistency))
747 public static ConsistencyLevel getConsistencyLevel(String consistency) {
748 return consistencyName.get(consistency.toUpperCase());
751 public static void loadProperties() throws Exception {
752 Properties prop = new Properties();
753 InputStream input = null;
755 // load the properties file
756 input = MusicUtil.class.getClassLoader().getResourceAsStream("music.properties");
758 } catch (Exception ex) {
759 logger.error(EELFLoggerDelegate.errorLogger, "Unable to find properties file.", ex);
760 throw new Exception();
765 } catch (IOException e) {
767 logger.error(EELFLoggerDelegate.errorLogger, e);
771 // get the property value and return it
772 MusicUtil.setMyCassaHost(prop.getProperty("cassandra.host"));
773 MusicUtil.setCassName(prop.getProperty("cassandra.user"));
774 MusicUtil.setCassPwd(prop.getProperty("cassandra.password"));
775 MusicUtil.setCassandraPort(Integer.parseInt(prop.getProperty("cassandra.port")));
776 MusicUtil.setNotifyTimeOut(Integer.parseInt(prop.getProperty("notify.timeout")));
777 MusicUtil.setNotifyInterval(Integer.parseInt(prop.getProperty("notify.interval")));
778 MusicUtil.setCacheObjectMaxLife(Integer.parseInt(prop.getProperty("cacheobject.maxlife")));
781 public static void setNotifyInterval(int notifyinterval) {
782 MusicUtil.notifyinterval = notifyinterval;
784 public static void setNotifyTimeOut(int notifytimeout) {
785 MusicUtil.notifytimeout = notifytimeout;
788 public static int getNotifyInterval() {
789 return MusicUtil.notifyinterval;
792 public static int getNotifyTimeout() {
793 return MusicUtil.notifytimeout;
796 public static int getCacheObjectMaxLife() {
797 return MusicUtil.cacheObjectMaxLife;
800 public static void setCacheObjectMaxLife(int cacheObjectMaxLife) {
801 MusicUtil.cacheObjectMaxLife = cacheObjectMaxLife;
805 * Given the time of write for an update in a critical section, this method provides a transformed timestamp
806 * that ensures that a previous lock holder who is still alive can never corrupt a later critical section.
807 * The main idea is to us the lock reference to clearly demarcate the timestamps across critical sections.
808 * @param the UUID lock reference associated with the write.
809 * @param the long timeOfWrite which is the actual time at which the write took place
810 * @throws MusicServiceException
811 * @throws MusicQueryException
813 public static long v2sTimeStampInMicroseconds(long ordinal, long timeOfWrite) throws MusicServiceException, MusicQueryException {
814 // TODO: use acquire time instead of music eternity epoch
815 long ts = ordinal * MaxLockReferenceTimePart + (timeOfWrite - MusicEternityEpochMillis);
820 public static MusicCoreService getMusicCoreService() {
821 if(getLockUsing().equals(MusicUtil.CASSANDRA))
822 return MusicCassaCore.getInstance();
824 return MusicCassaCore.getInstance();
831 public static Map<String, Object> validateLock(String lockName) {
832 Map<String, Object> resultMap = new HashMap<>();
833 String[] locks = lockName.split("\\.");
834 if(locks.length < 3) {
835 resultMap.put("Error", "Invalid lock. Please make sure lock is of the type keyspaceName.tableName.primaryKey");
838 String keyspace= locks[0];
839 if(keyspace.startsWith("$"))
840 keyspace = keyspace.substring(1);
841 resultMap.put("keyspace",keyspace);
846 public static void setIsCadi(boolean isCadi) {
847 MusicUtil.isCadi = isCadi;
850 public static void writeBackToQuorum(PreparedQueryObject selectQuery, String primaryKeyName,
851 PreparedQueryObject updateQuery, String keyspace, String table,
852 Object cqlFormattedPrimaryKeyValue)
855 ResultSet results = MusicDataStoreHandle.getDSHandle().executeQuorumConsistencyGet(selectQuery);
856 // write it back to a quorum
857 Row row = results.one();
858 ColumnDefinitions colInfo = row.getColumnDefinitions();
859 int totalColumns = colInfo.size();
861 StringBuilder fieldValueString = new StringBuilder("");
862 for (Definition definition : colInfo) {
863 String colName = definition.getName();
864 if (colName.equals(primaryKeyName))
866 DataType colType = definition.getType();
867 Object valueObj = MusicDataStoreHandle.getDSHandle().getColValue(row, colName, colType);
868 Object valueString = MusicUtil.convertToActualDataType(colType, valueObj);
869 fieldValueString.append(colName + " = ?");
870 updateQuery.addValue(valueString);
871 if (counter != (totalColumns - 1))
872 fieldValueString.append(",");
873 counter = counter + 1;
875 updateQuery.appendQueryString("UPDATE " + keyspace + "." + table + " SET "
876 + fieldValueString + " WHERE " + primaryKeyName + "= ? " + ";");
877 updateQuery.addValue(cqlFormattedPrimaryKeyValue);
879 MusicDataStoreHandle.getDSHandle().executePut(updateQuery, "critical");
880 } catch (MusicServiceException | MusicQueryException e) {
881 logger.error(EELFLoggerDelegate.errorLogger,e.getMessage(), AppMessages.QUERYERROR +""+updateQuery ,
882 ErrorSeverity.MAJOR, ErrorTypes.QUERYERROR, e);
886 public static boolean getIsCadi() {
887 return MusicUtil.isCadi;
892 * @return a random uuid
894 public static String generateUUID() {
895 String uuid = UUID.randomUUID().toString();
896 logger.info(EELFLoggerDelegate.applicationLogger,"New AID generated: "+uuid);