From e99b7fa829bf957c2a46223a1a20a32aebeda91b Mon Sep 17 00:00:00 2001 From: arthurdent3 Date: Fri, 2 Feb 2018 21:19:53 -0500 Subject: Initial code Import. Issue-ID: MUSIC-21 Change-Id: I89ceab0891b4b7cb999dab532d6bae9092f027cc Signed-off-by: arthurdent3 --- src/main/java/LICENSE.txt | 24 + .../java/org/onap/music/client/MusicClient.java | 604 +++++++++++ .../org/onap/music/client/MusicRestClient.java | 384 +++++++ .../org/onap/music/datastore/MusicDataStore.java | 391 +++++++ .../onap/music/datastore/PreparedQueryObject.java | 79 ++ .../music/datastore/jsonobjects/AAFResponse.java | 42 + .../music/datastore/jsonobjects/JsonDelete.java | 83 ++ .../music/datastore/jsonobjects/JsonInsert.java | 118 +++ .../music/datastore/jsonobjects/JsonKeySpace.java | 73 ++ .../datastore/jsonobjects/JsonLeasedLock.java | 49 + .../music/datastore/jsonobjects/JsonOnboard.java | 80 ++ .../music/datastore/jsonobjects/JsonSelect.java | 56 + .../music/datastore/jsonobjects/JsonTable.java | 113 ++ .../music/datastore/jsonobjects/JsonUpdate.java | 128 +++ .../music/datastore/jsonobjects/NameSpace.java | 47 + .../music/eelf/logging/EELFLoggerDelegate.java | 354 +++++++ .../music/exceptions/MusicLockingException.java | 74 ++ .../exceptions/MusicPolicyVoilationException.java | 79 ++ .../onap/music/exceptions/MusicQueryException.java | 89 ++ .../music/exceptions/MusicServiceException.java | 84 ++ .../onap/music/lockingservice/LockListener.java | 39 + .../onap/music/lockingservice/MusicLockState.java | 126 +++ .../music/lockingservice/MusicLockingService.java | 142 +++ .../onap/music/lockingservice/ProtocolSupport.java | 205 ++++ .../org/onap/music/lockingservice/ZNodeName.java | 117 +++ .../lockingservice/ZkStatelessLockService.java | 334 ++++++ .../music/lockingservice/ZooKeeperOperation.java | 42 + src/main/java/org/onap/music/main/CachingUtil.java | 370 +++++++ .../java/org/onap/music/main/CronJobManager.java | 47 + src/main/java/org/onap/music/main/MusicCore.java | 874 ++++++++++++++++ src/main/java/org/onap/music/main/MusicDigest.java | 78 ++ src/main/java/org/onap/music/main/MusicUtil.java | 465 +++++++++ .../org/onap/music/main/PropertiesListener.java | 147 +++ src/main/java/org/onap/music/main/ResultType.java | 39 + src/main/java/org/onap/music/main/ReturnType.java | 74 ++ .../response/jsonobjects/JsonLockResponse.java | 258 +++++ .../music/response/jsonobjects/JsonResponse.java | 95 ++ .../org/onap/music/rest/RestMusicAdminAPI.java | 305 ++++++ .../java/org/onap/music/rest/RestMusicBmAPI.java | 313 ++++++ .../java/org/onap/music/rest/RestMusicDataAPI.java | 1088 ++++++++++++++++++++ .../org/onap/music/rest/RestMusicLocksAPI.java | 209 ++++ .../java/org/onap/music/rest/RestMusicQAPI.java | 257 +++++ .../java/org/onap/music/rest/RestMusicTestAPI.java | 67 ++ .../org/onap/music/rest/RestMusicVersionAPI.java | 61 ++ 44 files changed, 8703 insertions(+) create mode 100644 src/main/java/LICENSE.txt create mode 100644 src/main/java/org/onap/music/client/MusicClient.java create mode 100644 src/main/java/org/onap/music/client/MusicRestClient.java create mode 100644 src/main/java/org/onap/music/datastore/MusicDataStore.java create mode 100644 src/main/java/org/onap/music/datastore/PreparedQueryObject.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/AAFResponse.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/JsonDelete.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/JsonInsert.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/JsonKeySpace.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/JsonLeasedLock.java create mode 100755 src/main/java/org/onap/music/datastore/jsonobjects/JsonOnboard.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/JsonSelect.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/JsonTable.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/JsonUpdate.java create mode 100644 src/main/java/org/onap/music/datastore/jsonobjects/NameSpace.java create mode 100644 src/main/java/org/onap/music/eelf/logging/EELFLoggerDelegate.java create mode 100644 src/main/java/org/onap/music/exceptions/MusicLockingException.java create mode 100644 src/main/java/org/onap/music/exceptions/MusicPolicyVoilationException.java create mode 100644 src/main/java/org/onap/music/exceptions/MusicQueryException.java create mode 100644 src/main/java/org/onap/music/exceptions/MusicServiceException.java create mode 100644 src/main/java/org/onap/music/lockingservice/LockListener.java create mode 100644 src/main/java/org/onap/music/lockingservice/MusicLockState.java create mode 100644 src/main/java/org/onap/music/lockingservice/MusicLockingService.java create mode 100644 src/main/java/org/onap/music/lockingservice/ProtocolSupport.java create mode 100644 src/main/java/org/onap/music/lockingservice/ZNodeName.java create mode 100644 src/main/java/org/onap/music/lockingservice/ZkStatelessLockService.java create mode 100644 src/main/java/org/onap/music/lockingservice/ZooKeeperOperation.java create mode 100755 src/main/java/org/onap/music/main/CachingUtil.java create mode 100644 src/main/java/org/onap/music/main/CronJobManager.java create mode 100644 src/main/java/org/onap/music/main/MusicCore.java create mode 100644 src/main/java/org/onap/music/main/MusicDigest.java create mode 100755 src/main/java/org/onap/music/main/MusicUtil.java create mode 100755 src/main/java/org/onap/music/main/PropertiesListener.java create mode 100644 src/main/java/org/onap/music/main/ResultType.java create mode 100644 src/main/java/org/onap/music/main/ReturnType.java create mode 100644 src/main/java/org/onap/music/response/jsonobjects/JsonLockResponse.java create mode 100644 src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java create mode 100755 src/main/java/org/onap/music/rest/RestMusicAdminAPI.java create mode 100644 src/main/java/org/onap/music/rest/RestMusicBmAPI.java create mode 100755 src/main/java/org/onap/music/rest/RestMusicDataAPI.java create mode 100644 src/main/java/org/onap/music/rest/RestMusicLocksAPI.java create mode 100755 src/main/java/org/onap/music/rest/RestMusicQAPI.java create mode 100644 src/main/java/org/onap/music/rest/RestMusicTestAPI.java create mode 100644 src/main/java/org/onap/music/rest/RestMusicVersionAPI.java (limited to 'src/main/java') diff --git a/src/main/java/LICENSE.txt b/src/main/java/LICENSE.txt new file mode 100644 index 00000000..cc6cdea5 --- /dev/null +++ b/src/main/java/LICENSE.txt @@ -0,0 +1,24 @@ + +The following license applies to all files in this and sub-directories. Licenses +are included in individual source files where appropriate, and if it differs +from this text, it supersedes this. Any file that does not have license text +defaults to being covered by this text; not all files support the addition of +licenses. +# +# ------------------------------------------------------------------------- +# Copyright (c) 2017 AT&T Intellectual Property +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------- +# \ No newline at end of file diff --git a/src/main/java/org/onap/music/client/MusicClient.java b/src/main/java/org/onap/music/client/MusicClient.java new file mode 100644 index 00000000..2d29e236 --- /dev/null +++ b/src/main/java/org/onap/music/client/MusicClient.java @@ -0,0 +1,604 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.client; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.lockingservice.MusicLockingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.ColumnDefinitions.Definition; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.KeyspaceMetadata; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.SimpleStatement; +import com.datastax.driver.core.Statement; +import com.datastax.driver.core.TableMetadata; +import com.datastax.driver.core.querybuilder.Clause; +import com.datastax.driver.core.querybuilder.Delete; +import com.datastax.driver.core.querybuilder.QueryBuilder; +import com.datastax.driver.core.querybuilder.Select; + +/** + * A MUSIC client that talks directly to Cassandra/ZooKeeper. This was taken, and slightly modified, + * from the REST version of the code. + * + * @author Robert Eby + */ +public class MusicClient { + private static final Logger LOG = LoggerFactory.getLogger(MusicClient.class); + + private final String[] music_hosts; // array of hosts in the music cluster + private Cluster cluster; // MUSIC Cassandra cluster + private Session session; // MUSIC Cassandra session + private MusicLockingService mls; // ZooKeeper + private final Set lockNames;// set of active lock names + + /** + * Create a MUSIC client that talks to MUSIC on localhost. + */ + public MusicClient() { + this("127.0.0.1"); + } + + /** + * Create a MUSIC client that talks to MUSIC on a remote host. The string hosts is a + * comma-separated list of IP addresses for remote instances of Cassandra/ZooKeeper. + * + * @param hosts the list of hostnames + */ + public MusicClient(String hosts) { + music_hosts = hosts.split(","); + if (cluster == null) { + LOG.debug("Initializing MUSIC Client with endpoints " + hosts); + cluster = Cluster.builder().addContactPoints(music_hosts).build(); + } + session = cluster.connect(); + mls = null; + lockNames = new HashSet(); + } + + /** + * Close the connection to MUSIC. + */ + public void close() { + if (session != null) { + session.close(); + session = null; + } + if (cluster != null) { + cluster.close(); + cluster = null; + } + } + + /** + * Be sure to close the connection to MUSIC when this object is GC-ed. + */ + @Override + protected void finalize() { + close(); + } + + /** + * Return a String representation of the music hosts used by this object. + * + * @return the string + */ + @Override + public String toString() { + List t = Arrays.asList(music_hosts); + return "MUSIC hosts=" + t.toString(); + } + + /** + * Create a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#createLock(String) + * @param lockName the lock name + * @return FILL IN + */ + public String createLock(String lockName) { + String ln = "/" + lockName; + synchronized (lockNames) { + lockNames.add(ln); + } + return getLockingService().createLockId(ln); + } + + /** + * Acquire a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#lock(String) + * @param lockName the lock name + * @return FILL IN + */ + public boolean acquireLock(String lockName) { + return getLockingService().isMyTurn(lockName); + } + + /** + * Get the lock holder. + * + * @see org.onap.music.lockingservice.MusicLockingService#currentLockHolder(String) + * @param lockName the lock name + * @return FILL IN + */ + public String getLockHolder(String lockName) { + return getLockingService().whoseTurnIsIt("/" + lockName); + } + + /** + * Unlock a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#unlock(String) + * @param lockName the lock name + */ + public void unlockLock(String lockName) { + getLockingService().unlockAndDeleteId(lockName); + } + + /** + * Delete a lock. + * + * @see org.onap.music.lockingservice.MusicLockingService#deleteLock(String) + * @param lockName the lock name + */ + public void deleteLock(String lockName) { + String ln = "/" + lockName; + synchronized (lockNames) { + lockNames.remove(ln); + } + getLockingService().deleteLock(ln); + } + + /** + * Delete all locks. + * + * @see org.onap.music.lockingservice.MusicLockingService#deleteLock(String) + * @return true + */ + public boolean deleteAllLocks() { + synchronized (lockNames) { + for (String lockName : lockNames) { + deleteLock(lockName); + } + lockNames.clear(); + } + return true; + } + + /** + * Create a keyspace using the default replication configuration. + * + * @param keyspaceName the name of the keyspace + * @return always true currently + * @throws Exception Cassandra exceptions are passed through + */ + public boolean createKeyspace(String keyspaceName) throws Exception { + Map repl = new HashMap(); + repl.put("class", "SimpleStrategy"); + repl.put("replication_factor", 1); + Map consistencyInfo = Collections.singletonMap("type", "eventual"); + JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + jsonKp.setDurabilityOfWrites("true"); + jsonKp.setReplicationInfo(repl); + return createKeyspace(keyspaceName, jsonKp); + } + + public boolean createKeyspace(String keyspaceName, JsonKeySpace kspObject) throws Exception { + String consistency = extractConsistencyInfo(keyspaceName, kspObject.getConsistencyInfo()); + Map replicationInfo = kspObject.getReplicationInfo(); + String durability = ""; + if (kspObject.getDurabilityOfWrites() != null) + durability = " AND durable_writes = " + kspObject.getDurabilityOfWrites(); + String query = String.format( + "CREATE KEYSPACE IF NOT EXISTS %s WITH replication = { %s } %s;", + keyspaceName, jsonMaptoSqlString(replicationInfo, ","), durability); + LOG.debug(query); + executeCreateQuery(query, consistency); + return true; + } + + public boolean dropKeyspace(String keyspaceName, JsonKeySpace kspObject) throws Exception { + String consistency = extractConsistencyInfo(keyspaceName, kspObject.getConsistencyInfo()); + String query = String.format("DROP KEYSPACE %s;", keyspaceName); + LOG.debug(query); + executeCreateQuery(query, consistency); + return false; + } + + public boolean createTable(String tablename, Map cols) throws Exception { + JsonTable tableObj = new JsonTable(); + Map map = new HashMap(); // This should be in the + // consutructor! + map.put("type", "eventual"); + tableObj.setConsistencyInfo(map); + return createTable(tablename, cols, tableObj); + } + + public boolean createTable(String tablename, Map cols, JsonTable tableObj) + throws Exception { + // Note: https://docs.datastax.com/en/cql/3.0/cql/cql_reference/create_table_r.html + + // first read the information about the table fields + StringBuilder fields = new StringBuilder(); + String prefix = ""; + for (String key : cols.keySet()) { + fields.append(prefix).append(key).append(" ").append(cols.get(key)); + prefix = ", "; + } + + // information about the name-value style properties + // Map propertiesMap = tableObj.getProperties(); + // String propertiesString=""; + // if(propertiesMap != null){ + // counter =0; + // for (Map.Entry entry : propertiesMap.entrySet()) + // { + // Object ot = entry.getValue(); + // String value = ot+""; + // if(ot instanceof String){ + // value = "'"+value+"'"; + // }else if(ot instanceof Map){ + // Map otMap = (Map)ot; + // value = "{"+jsonMaptoSqlString(otMap, ",")+"}"; + // } + // propertiesString = propertiesString+entry.getKey()+"="+ value+""; + // if(counter!=propertiesMap.size()-1) + // propertiesString = propertiesString+" AND "; + // counter = counter +1; + // } + // } + + String query = String.format("CREATE TABLE IF NOT EXISTS %s (%s);", tablename, + fields.toString()); + // if (propertiesMap != null) + // query = query + " WITH "+ propertiesString; + + LOG.debug(query); + String consistency = extractConsistencyInfo(tablename, tableObj.getConsistencyInfo()); + executeCreateQuery(query, consistency); + return false; + } + + public boolean dropTable(String name) { + // TODO + return false; + } + + public boolean insertRow(String name, Map valuesMap) throws Exception { + Map consistencyInfo = Collections.singletonMap("type", "eventual"); + return insertRow(name, valuesMap, consistencyInfo, new JsonInsert()); + } + + public boolean insertRow(String tablename, Map valuesMap, + Map consistencyInfo, JsonInsert insObj) throws Exception { + // Note: https://docs.datastax.com/en/cql/3.0/cql/cql_reference/insert_r.html + String[] parts = tablename.split("\\."); + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(parts[0]); + TableMetadata tableInfo = ks.getTable(parts[1]); + + StringBuilder fields = new StringBuilder(); + StringBuilder values = new StringBuilder(); + String prefix = ""; + for (String key : valuesMap.keySet()) { + fields.append(prefix).append(key); + Object valueObj = valuesMap.get(key); + DataType colType = tableInfo.getColumn(key).getType(); + values.append(prefix).append(convertToSqlDataType(colType, valueObj)); + prefix = ", "; + } + + String suffix = getTTLSuffix(insObj); + String query = String.format("INSERT INTO %s (%s) VALUES (%s)%s;", tablename, + fields.toString(), values.toString(), suffix); + LOG.debug(query); + + String consistency = extractConsistencyInfo(tablename, consistencyInfo); + executeCreateQuery(query, consistency); + return false; + } + + public boolean lockRow(String name, Map cols) { + // TODO + return false; + } + + /** + * Select ALL rows in the table. + * + * @param tablename the name of the table + * @return a list of maps, one map per row + */ + public List> selectRows(final String tablename) { + return selectRows(tablename, new HashMap()); + } + + public List> selectRows(final String tablename, Map cols) { + String ns = ""; + String tbl = tablename; + int ix = tbl.indexOf('.'); + if (ix >= 0) { + ns = tablename.substring(0, ix); + tbl = tablename.substring(ix + 1); + } + Select sel = QueryBuilder.select().all().from(ns, tbl); + Statement stmt = sel; + if (cols.size() == 1) { + // only handles 1 WHERE value right now + String k = cols.keySet().iterator().next(); + Clause eqclause = QueryBuilder.eq(k, cols.get(k)); + stmt = sel.where(eqclause); + } + ResultSet resultset = session.execute(stmt); + List> results = new ArrayList>(); + for (Row row : resultset) { + Map map = new HashMap(); + for (Definition definition : row.getColumnDefinitions()) { + map.put(definition.getName(), + readRow(row, definition.getName(), definition.getType())); + } + results.add(map); + } + return results; + } + + private Object readRow(final Row row, final String name, final DataType colType) { + switch (colType.getName()) { + case BIGINT: + return row.getLong(name); + case BOOLEAN: + return row.getBool(name); + case DOUBLE: + return row.getDouble(name); + case FLOAT: + return row.getFloat(name); + case INT: + return row.getInt(name); + case MAP: + return row.getMap(name, String.class, String.class); + case UUID: + return row.getUUID(name); + case TEXT: + case VARCHAR: + return row.getString(name); + case VARINT: + return row.getVarint(name); + // These are not supported right now.... + // ASCII + // BLOB + // COUNTER + // CUSTOM + // DECIMAL + // INET + // LIST + // SET + // TIMESTAMP + // TIMEUUID + // TUPLE + // UDT + default: + return null; + } + } + + @Deprecated + public List> OLDselectRows(String tablename, Map cols) { + String query = String.format("SELECT * FROM %s", tablename); + if (cols.size() > 0) { + // add WHERE clause + // String[] parts = tablename.split("\\."); + // KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(parts[0]); + // TableMetadata tableInfo = ks.getTable(parts[1]); + String whereclause = " WHERE"; + String prefix = ""; + for (String key : cols.keySet()) { + String val = cols.get(key); + // DataType colType = tableInfo.getColumn(key).getType(); + whereclause = String.format("%s%s %s = '%s'", whereclause, prefix, key, val); + prefix = " AND"; + } + query += whereclause; + } + LOG.debug(query); + ResultSet resultset = session.execute(query); + List> results = new ArrayList>(); + for (Row row : resultset) { + ColumnDefinitions colInfo = row.getColumnDefinitions(); + Map map = new HashMap(); + for (Definition definition : colInfo) { + // map.put(definition.getName(), (String)MusicDataStore.readRow(row, + // definition.getName(), definition.getType())); + } + results.add(map); + } + return results; + } + + public void updateRows(String tablename, Map cols, Map vals) + throws Exception { + Map consistencyInfo = Collections.singletonMap("type", "eventual"); + updateRows(tablename, cols, vals, consistencyInfo, new JsonInsert()); + } + + public void updateRows(String tablename, Map cols, Map vals, + Map consistencyInfo, JsonInsert insObj) throws Exception { + // https://docs.datastax.com/en/cql/3.0/cql/cql_reference/update_r.html + + // obtain the field value pairs of the update + String[] parts = tablename.split("\\."); + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(parts[0]); + TableMetadata tableInfo = ks.getTable(parts[1]); + + StringBuilder fields = new StringBuilder(); + String prefix = ""; + for (String key : vals.keySet()) { + Object valueObj = vals.get(key); + String valueString = convertToSqlDataType(tableInfo.getColumn(key).getType(), valueObj); + fields.append(prefix).append(key).append(" = ").append(valueString); + prefix = ", "; + } + + // get the row specifier + StringBuilder rows = new StringBuilder(); + String primaryKey = ""; + prefix = ""; + for (String key : cols.keySet()) { + String indValue = cols.get(key); + DataType colType = tableInfo.getColumn(key).getType(); + String formattedValue = convertToSqlDataType(colType, indValue); + primaryKey = primaryKey + indValue; + rows.append(prefix).append(key).append(" = ").append(formattedValue); + prefix = " AND "; + } + + String using = getTTLSuffix(insObj); + String query = String.format("UPDATE %s%s SET %s WHERE %s;", tablename, using, + fields.toString(), rows.toString()); + LOG.debug(query); + + String consistency = extractConsistencyInfo(tablename, consistencyInfo); + executeCreateQuery(query, consistency); + } + + public void deleteRows(String tablename, Map cols) { + String ns = ""; + String tbl = tablename; + int ix = tbl.indexOf('.'); + if (ix >= 0) { + ns = tablename.substring(0, ix); + tbl = tablename.substring(ix + 1); + } + Delete stmt = QueryBuilder.delete().from(ns, tbl); + if (cols.size() == 1) { + // only handles 1 WHERE value right now + String k = cols.keySet().iterator().next(); + Clause eqclause = QueryBuilder.eq(k, cols.get(k)); + session.execute(stmt.where(eqclause)); + } else { + session.execute(stmt); + } + } + + private String getTTLSuffix(JsonInsert insObj) { + String ttl = insObj.getTtl(); + String timestamp = insObj.getTimestamp(); + if (ttl != null && ttl.length() > 0) { + if (timestamp != null && timestamp.length() > 0) { + return " USING TTL " + ttl + " AND TIMESTAMP " + timestamp; + } else { + return " USING TTL " + ttl; + } + } else if (timestamp != null && timestamp.length() > 0) { + return " USING TIMESTAMP " + timestamp; + } + return ""; + } + + private MusicLockingService getLockingService() { + if (mls == null) { + mls = new MusicLockingService(music_hosts[0]); + } + return mls; + } + + private String extractConsistencyInfo(String key, Map consistencyInfo) + throws Exception { + String consistency = ""; + if (consistencyInfo.get("type").equalsIgnoreCase("atomic")) { + String lockId = consistencyInfo.get("lockId"); + String lockName = lockId.substring(lockId.indexOf("$") + 1); + lockName = lockName.substring(0, lockName.indexOf("$")); + + // first ensure that the lock name is correct before seeing if it has access + if (!lockName.equalsIgnoreCase(key)) + throw new Exception("THIS LOCK IS NOT FOR THE KEY: " + key); + + String lockStatus = getLockingService().isMyTurn(lockId) + ""; + if (lockStatus.equalsIgnoreCase("false")) + throw new Exception("YOU DO NOT HAVE THE LOCK"); + return "atomic"; + } + if (consistencyInfo.get("type").equalsIgnoreCase("eventual")) + return "eventual"; + throw new Exception("Consistency type " + consistency + " unknown!!"); + } + + // utility function to parse json map into sql like string + private String jsonMaptoSqlString(Map jMap, String lineDelimiter) { + String sql = ""; + String prefix = ""; + for (Map.Entry entry : jMap.entrySet()) { + Object ot = entry.getValue(); + String value = ot + ""; + if (ot instanceof String) { + value = "'" + value + "'"; + } + sql = String.format("%s%s'%s': %s", sql, prefix, entry.getKey(), value); + prefix = lineDelimiter; + } + return sql; + } + + private String convertToSqlDataType(DataType type, Object valueObj) { + switch (type.getName()) { + case TEXT: + String t = valueObj.toString(); + t = t.replaceAll("'", "''"); + return "'" + t + "'"; + case MAP: + @SuppressWarnings("unchecked") + Map otMap = (Map) valueObj; + return "{" + jsonMaptoSqlString(otMap, ",") + "}"; + default: + case UUID: + return valueObj.toString(); + } + } + + private void executeCreateQuery(String query, String consistency) throws Exception { + Statement statement = new SimpleStatement(query); + if (consistency.equalsIgnoreCase("atomic")) + statement.setConsistencyLevel(ConsistencyLevel.ALL); + else if (consistency.equalsIgnoreCase("eventual")) + statement.setConsistencyLevel(ConsistencyLevel.ONE); + else + throw new Exception("Consistency level " + consistency + " unknown!!"); + session.execute(statement); + } +} diff --git a/src/main/java/org/onap/music/client/MusicRestClient.java b/src/main/java/org/onap/music/client/MusicRestClient.java new file mode 100644 index 00000000..93a2e12b --- /dev/null +++ b/src/main/java/org/onap/music/client/MusicRestClient.java @@ -0,0 +1,384 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.client; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import javax.ws.rs.core.MediaType; +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.main.MusicUtil; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.api.json.JSONConfiguration; + +public class MusicRestClient { + String[] musicNodes; + + public MusicRestClient(String[] musicNodes) { + this.musicNodes = musicNodes; + } + + public MusicRestClient(String oneMusicNode) { + musicNodes = new String[1]; + this.musicNodes[0] = oneMusicNode; + } + + public String getMusicNodeURL() { + String musicurl = "http://" + getMusicNodeIp() + ":8080/MUSIC/rest"; + return musicurl; + } + + private String getMusicNodeIp() { + Random r = new Random(); + int index = r.nextInt(musicNodes.length); + return musicNodes[index]; + } + + public void createKeyspace(String keyspaceName) { + Map replicationInfo = new HashMap(); + replicationInfo.put("class", "SimpleStrategy"); + replicationInfo.put("replication_factor", 1); + String durabilityOfWrites = "false"; + Map consistencyInfo = new HashMap(); + consistencyInfo.put("type", "eventual"); + org.onap.music.datastore.jsonobjects.JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + jsonKp.setDurabilityOfWrites(durabilityOfWrites); + jsonKp.setReplicationInfo(replicationInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client.resource(getMusicNodeURL() + "/keyspaces/" + keyspaceName); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jsonKp); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + public void createStringMapTable(String keyspaceName, String tableName, + Map fields) { + Map consistencyInfo = new HashMap(); + consistencyInfo.put("type", "eventual"); + + JsonTable jtab = new JsonTable(); + jtab.setFields(fields); + jtab.setConsistencyInfo(consistencyInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jtab); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + } + + public void checkMusicVersion() { + Client client = Client.create(); + + WebResource webResource = client.resource(getMusicNodeURL() + "/version"); + + ClientResponse response = webResource.accept("text/plain").get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + } + + public void createRow(String keyspaceName, String tableName, Map values) { + Map consistencyInfo = new HashMap(); + consistencyInfo.put("type", "eventual"); + + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows"; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .post(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus() + "url:" + + url + "values:" + values); + + + } + + private void basicUpdateRow(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map values, + Map consistencyInfo) { + JsonInsert jIns = new JsonInsert(); + jIns.setValues(values); + jIns.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows?" + primaryKeyName + "=" + primaryKeyValue; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .put(ClientResponse.class, jIns); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus() + "url:" + + url + " values:" + values); + + } + + public void updateEntry(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map values) { + Map consistencyInfo = new HashMap(); + consistencyInfo.put("type", "eventual"); + basicUpdateRow(keyspaceName, tableName, primaryKeyName, primaryKeyValue, values, + consistencyInfo); + } + + public void deleteEntry(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue) { + Map consistencyInfo = new HashMap(); + consistencyInfo.put("type", "eventual"); + + JsonDelete jDel = new JsonDelete(); + jDel.setConsistencyInfo(consistencyInfo); + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows?" + primaryKeyName + "=" + primaryKeyValue; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").type("application/json") + .delete(ClientResponse.class, jDel); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + url); + + } + + public Map readRow(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue) { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows?" + primaryKeyName + "=" + primaryKeyValue; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map output = response.getEntity(Map.class); + return output; + } + + public Map readAllRows(String keyspaceName, String tableName) { + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + String url = getMusicNodeURL() + "/keyspaces/" + keyspaceName + "/tables/" + tableName + + "/rows"; + WebResource webResource = client.resource(url); + + ClientResponse response = webResource.accept("application/json").get(ClientResponse.class); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + + Map output = response.getEntity(Map.class); + return output; + } + + + public void dropKeySpace(String keyspaceName) { + Map consistencyInfo = new HashMap(); + consistencyInfo.put("type", "eventual"); + + JsonKeySpace jsonKp = new JsonKeySpace(); + jsonKp.setConsistencyInfo(consistencyInfo); + + ClientConfig clientConfig = new DefaultClientConfig(); + + clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); + + Client client = Client.create(clientConfig); + + WebResource webResource = client.resource(getMusicNodeURL() + "/keyspaces/" + keyspaceName); + + ClientResponse response = + webResource.type("application/json").delete(ClientResponse.class, jsonKp); + + if (response.getStatus() < 200 || response.getStatus() > 299) + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + public String createLock(String primaryKeyValue) { + Client client = Client.create(); + String msg = getMusicNodeURL() + "/locks/create/" + primaryKeyValue; + WebResource webResource = client.resource(msg); + + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.post(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + msg); + } + + String output = response.getEntity(String.class); + + return output; + } + + public boolean acquireLock(String lockId) { + Client client = Client.create(); + String msg = getMusicNodeURL() + "/locks/acquire/" + lockId; + WebResource webResource = client.resource(msg); + + + WebResource.Builder wb = webResource.accept(MediaType.TEXT_PLAIN); + + ClientResponse response = wb.get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException( + "Failed : HTTP error code : " + response.getStatus() + "url:" + msg); + } + + String output = response.getEntity(String.class); + Boolean status = Boolean.parseBoolean(output); + return status; + } + + public void releaseLock(String lockId) { + Client client = Client.create(); + WebResource webResource = client.resource(getMusicNodeURL() + "/locks/release/" + lockId); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + } + + + public void updateRowAtomically(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map values) { + /* + * create lock for the candidate. The music API dictates that the lock name must be of the + * form keyspacename.tableName.primaryKeyName + */ + + String lockName = keyspaceName + "." + tableName + "." + primaryKeyValue; + String lockId = createLock(lockName); + while (acquireLock(lockId) != true); + + + Map consistencyInfo = new HashMap(); + consistencyInfo.put("type", "atomic"); + consistencyInfo.put("lockId", lockId); + + basicUpdateRow(keyspaceName, tableName, primaryKeyName, primaryKeyValue, values, + consistencyInfo); + + // release lock now that the operation is done + releaseLock(lockId); + + } + + public String getMusicId() { + + Client client = Client.create(); + + WebResource webResource = client.resource(getMusicNodeURL() + "/nodeId"); + ClientResponse response = webResource.accept("text/plain").get(ClientResponse.class); + + if (response.getStatus() != 200) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + + String output = response.getEntity(String.class); + return output; + } + + public void deleteRowAtomically(String keyspaceName, String tableName, String primaryKeyName, + String primaryKeyValue, Map values) { + + } + + public void deleteLock(String lockName) { + Client client = Client.create(); + WebResource webResource = client.resource(getMusicNodeURL() + "/locks/delete/" + lockName); + + ClientResponse response = webResource.delete(ClientResponse.class); + + + if (response.getStatus() != 204) { + throw new RuntimeException("Failed : HTTP error code : " + response.getStatus()); + } + } +} + diff --git a/src/main/java/org/onap/music/datastore/MusicDataStore.java b/src/main/java/org/onap/music/datastore/MusicDataStore.java new file mode 100644 index 00000000..c67c72e1 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/MusicDataStore.java @@ -0,0 +1,391 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicUtil; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.ColumnDefinitions.Definition; +import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.KeyspaceMetadata; +import com.datastax.driver.core.Metadata; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.TableMetadata; +import com.datastax.driver.core.exceptions.NoHostAvailableException; + +/** + * @author nelson24 + * + */ +public class MusicDataStore { + private Session session; + private Cluster cluster; + + + + /** + * @param session + */ + public void setSession(Session session) { + this.session = session; + } + + /** + * @param cluster + */ + public void setCluster(Cluster cluster) { + this.cluster = cluster; + } + + /** + * + */ + private static EELFLogger logger = EELFManager.getInstance().getLogger(MusicDataStore.class); + + /** + * + */ + public MusicDataStore() { + connectToCassaCluster(); + } + + + /** + * @param cluster + * @param session + */ + public MusicDataStore(Cluster cluster, Session session) { + this.session = session; + this.cluster = cluster; + } + + /** + * + * @param remoteIp + * @throws MusicServiceException + */ + public MusicDataStore(String remoteIp) { + try { + connectToCassaCluster(remoteIp); + } catch (MusicServiceException e) { + logger.error(e.getMessage()); + } + } + + /** + * + * @return + */ + private ArrayList getAllPossibleLocalIps() { + ArrayList allPossibleIps = new ArrayList(); + try { + Enumeration en = NetworkInterface.getNetworkInterfaces(); + while (en.hasMoreElements()) { + NetworkInterface ni = (NetworkInterface) en.nextElement(); + Enumeration ee = ni.getInetAddresses(); + while (ee.hasMoreElements()) { + InetAddress ia = (InetAddress) ee.nextElement(); + allPossibleIps.add(ia.getHostAddress()); + } + } + } catch (SocketException e) { + logger.error(e.getMessage()); + } + return allPossibleIps; + } + + /** + * This method iterates through all available IP addresses and connects to multiple cassandra + * clusters. + */ + private void connectToCassaCluster() { + Iterator it = getAllPossibleLocalIps().iterator(); + String address = "localhost"; + logger.info("Connecting to cassa cluster: Iterating through possible ips:" + + getAllPossibleLocalIps()); + while (it.hasNext()) { + try { + cluster = Cluster.builder().withPort(9042) + .withCredentials(MusicUtil.getCassName(), MusicUtil.getCassPwd()) + .addContactPoint(address).build(); + Metadata metadata = cluster.getMetadata(); + logger.info("Connected to cassa cluster " + metadata.getClusterName() + " at " + + address); + session = cluster.connect(); + + break; + } catch (NoHostAvailableException e) { + address = it.next(); + logger.error(e.getMessage()); + } + } + } + + /** + * + */ + public void close() { + session.close(); + } + + /** + * This method connects to cassandra cluster on specific address. + * + * @param address + */ + private void connectToCassaCluster(String address) throws MusicServiceException { + cluster = Cluster.builder().withPort(9042) + .withCredentials(MusicUtil.getCassName(), MusicUtil.getCassPwd()) + .addContactPoint(address).build(); + Metadata metadata = cluster.getMetadata(); + logger.info("Connected to cassa cluster " + metadata.getClusterName() + " at " + address); + try { + session = cluster.connect(); + } catch (Exception ex) { + logger.error(ex.getMessage()); + throw new MusicServiceException( + "Error while connecting to Cassandra cluster.. " + ex.getMessage()); + } + } + + /** + * + * @param keyspace + * @param tableName + * @param columnName + * @return DataType + */ + public DataType returnColumnDataType(String keyspace, String tableName, String columnName) { + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(keyspace); + TableMetadata table = ks.getTable(tableName); + return table.getColumn(columnName).getType(); + + } + + /** + * + * @param keyspace + * @param tableName + * @return TableMetadata + */ + public TableMetadata returnColumnMetadata(String keyspace, String tableName) { + KeyspaceMetadata ks = cluster.getMetadata().getKeyspace(keyspace); + return ks.getTable(tableName); + } + + + /** + * Utility function to return the Java specific object type. + * + * @param row + * @param colName + * @param colType + * @return + */ + public Object getColValue(Row row, String colName, DataType colType) { + + switch (colType.getName()) { + case VARCHAR: + return row.getString(colName); + case UUID: + return row.getUUID(colName); + case VARINT: + return row.getVarint(colName); + case BIGINT: + return row.getLong(colName); + case INT: + return row.getInt(colName); + case FLOAT: + return row.getFloat(colName); + case DOUBLE: + return row.getDouble(colName); + case BOOLEAN: + return row.getBool(colName); + case MAP: + return row.getMap(colName, String.class, String.class); + default: + return null; + } + } + + public boolean doesRowSatisfyCondition(Row row, Map condition) { + ColumnDefinitions colInfo = row.getColumnDefinitions(); + + for (Map.Entry entry : condition.entrySet()) { + String colName = entry.getKey(); + DataType colType = colInfo.getType(colName); + Object columnValue = getColValue(row, colName, colType); + Object conditionValue = MusicUtil.convertToActualDataType(colType, entry.getValue()); + if (columnValue.equals(conditionValue) == false) + return false; + } + return true; + } + + /** + * Utility function to store ResultSet values in to a MAP for output. + * + * @param results + * @return MAP + */ + public Map> marshalData(ResultSet results) { + Map> resultMap = + new HashMap>(); + int counter = 0; + for (Row row : results) { + ColumnDefinitions colInfo = row.getColumnDefinitions(); + HashMap resultOutput = new HashMap(); + for (Definition definition : colInfo) { + if (!definition.getName().equals("vector_ts")) + resultOutput.put(definition.getName(), + getColValue(row, definition.getName(), definition.getType())); + } + resultMap.put("row " + counter, resultOutput); + counter++; + } + return resultMap; + } + + + // Prepared Statements 1802 additions + /** + * This Method performs DDL and DML operations on Cassandra using specified consistency level + * + * @param queryObject Object containing cassandra prepared query and values. + * @param consistency Specify consistency level for data synchronization across cassandra + * replicas + * @return Boolean Indicates operation success or failure + * @throws MusicServiceException + * @throws MusicQueryException + */ + public boolean executePut(PreparedQueryObject queryObject, String consistency) + throws MusicServiceException, MusicQueryException { + + boolean result = false; + + if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) { + logger.error("Error while processing prepared query object"); + throw new MusicQueryException("Ill formed queryObject for the request = " + "[" + + queryObject.getQuery() + "]"); + } + logger.info("In preprared Execute Put: the actual insert query:" + queryObject.getQuery() + + "; the values" + queryObject.getValues()); + PreparedStatement preparedInsert = session.prepare(queryObject.getQuery()); + try { + if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + logger.info("Executing critical put query"); + preparedInsert.setConsistencyLevel(ConsistencyLevel.QUORUM); + } else if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) { + logger.info("Executing simple put query"); + preparedInsert.setConsistencyLevel(ConsistencyLevel.ONE); + } + + session.execute(preparedInsert.bind(queryObject.getValues().toArray())); + result = true; + } catch (Exception e) { + logger.error("Executing Session Failure for Request = " + "[" + queryObject.getQuery() + + "]" + " Reason = " + e.getMessage()); + throw new MusicServiceException("Executing Session Failure for Request = " + "[" + + queryObject.getQuery() + "]" + " Reason = " + e.getMessage()); + } + + + return result; + } + + /** + * This method performs DDL operations on Cassandra using consistency level ONE. + * + * @param queryObject Object containing cassandra prepared query and values. + * @return ResultSet + * @throws MusicServiceException + * @throws MusicQueryException + */ + public ResultSet executeEventualGet(PreparedQueryObject queryObject) + throws MusicServiceException, MusicQueryException { + + if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) { + throw new MusicQueryException("Ill formed queryObject for the request = " + "[" + + queryObject.getQuery() + "]"); + } + logger.info("Executing Eventual get query:" + queryObject.getQuery()); + PreparedStatement preparedEventualGet = session.prepare(queryObject.getQuery()); + preparedEventualGet.setConsistencyLevel(ConsistencyLevel.ONE); + ResultSet results = null; + try { + results = session.execute(preparedEventualGet.bind(queryObject.getValues().toArray())); + + } catch (Exception ex) { + logger.error(ex.getMessage()); + throw new MusicServiceException(ex.getMessage()); + } + return results; + } + + /** + * + * This method performs DDL operation on Cassandra using consistency level QUORUM. + * + * @param queryObject Object containing cassandra prepared query and values. + * @return ResultSet + * @throws MusicServiceException + * @throws MusicQueryException + */ + public ResultSet executeCriticalGet(PreparedQueryObject queryObject) + throws MusicServiceException, MusicQueryException { + if (!MusicUtil.isValidQueryObject(!queryObject.getValues().isEmpty(), queryObject)) { + logger.error("Error processing Prepared Query Object"); + throw new MusicQueryException("Ill formed queryObject for the request = " + "[" + + queryObject.getQuery() + "]"); + } + logger.info("Executing Critical get query:" + queryObject.getQuery()); + PreparedStatement preparedEventualGet = session.prepare(queryObject.getQuery()); + preparedEventualGet.setConsistencyLevel(ConsistencyLevel.QUORUM); + ResultSet results = null; + try { + results = session.execute(preparedEventualGet.bind(queryObject.getValues().toArray())); + } catch (Exception ex) { + logger.error(ex.getMessage()); + throw new MusicServiceException(ex.getMessage()); + } + return results; + + } + + +} diff --git a/src/main/java/org/onap/music/datastore/PreparedQueryObject.java b/src/main/java/org/onap/music/datastore/PreparedQueryObject.java new file mode 100644 index 00000000..694d9acd --- /dev/null +++ b/src/main/java/org/onap/music/datastore/PreparedQueryObject.java @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author srupane + * + */ +public class PreparedQueryObject { + + + private List values; + private StringBuilder query; + + + + /** + * + */ + public PreparedQueryObject() { + + this.values = new ArrayList<>(); + this.query = new StringBuilder(); + } + + /** + * @return + */ + public List getValues() { + return values; + } + + /** + * @param o + */ + public void addValue(Object o) { + this.values.add(o); + } + + /** + * @param s + */ + public void appendQueryString(String s) { + this.query.append(s); + } + + /** + * @return + */ + public String getQuery() { + return this.query.toString(); + } + + + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/AAFResponse.java b/src/main/java/org/onap/music/datastore/jsonobjects/AAFResponse.java new file mode 100644 index 00000000..df6089ee --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/AAFResponse.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.ArrayList; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Reponse class for AAF request") +public class AAFResponse { + + private ArrayList ns = null; + + @ApiModelProperty(value = "Namespace value") + public ArrayList getNs() { + return ns; + } + + public void setNs(ArrayList ns) { + this.ns = ns; + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonDelete.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonDelete.java new file mode 100644 index 00000000..87c000b2 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonDelete.java @@ -0,0 +1,83 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.ArrayList; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model for delete") +public class JsonDelete { + + private ArrayList columns = null; + private Map consistencyInfo; + private Map conditions; + String ttl, timestamp; + + + @ApiModelProperty(value = "Conditions") + public Map getConditions() { + return conditions; + } + + public void setConditions(Map conditions) { + this.conditions = conditions; + } + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Column values") + public ArrayList getColumns() { + return columns; + } + + public void setColumns(ArrayList columns) { + this.columns = columns; + } + + + @ApiModelProperty(value = "Time to live information") + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + + @ApiModelProperty(value = "Time stamp") + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonInsert.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonInsert.java new file mode 100644 index 00000000..22c0104b --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonInsert.java @@ -0,0 +1,118 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model for table vlaues insert") +public class JsonInsert implements Serializable { + private String keyspaceName; + private String tableName; + private Map values; + String ttl, timestamp; + private Map row_specification; + private Map consistencyInfo; + + @ApiModelProperty(value = "keyspace") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @ApiModelProperty(value = "Table name") + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Time to live information") + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + + @ApiModelProperty(value = "Time stamp") + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + @ApiModelProperty(value = "values returned") + public Map getValues() { + return values; + } + + public void setValues(Map values) { + this.values = values; + } + + @ApiModelProperty(value = "Information for selecting specific rows for insert") + public Map getRow_specification() { + return row_specification; + } + + public void setRow_specification(Map row_specification) { + this.row_specification = row_specification; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return bos.toByteArray(); + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonKeySpace.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonKeySpace.java new file mode 100644 index 00000000..01b72672 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonKeySpace.java @@ -0,0 +1,73 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model creating new keyspace") +public class JsonKeySpace { + private String keyspaceName; + private Map replicationInfo; + private String durabilityOfWrites; + private Map consistencyInfo; + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Replication information") + public Map getReplicationInfo() { + return replicationInfo; + } + + public void setReplicationInfo(Map replicationInfo) { + this.replicationInfo = replicationInfo; + } + + @ApiModelProperty(value = "Durability", allowableValues = "true,false") + public String getDurabilityOfWrites() { + return durabilityOfWrites; + } + + public void setDurabilityOfWrites(String durabilityOfWrites) { + this.durabilityOfWrites = durabilityOfWrites; + } + + @ApiModelProperty(value = "Keyspace name") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonLeasedLock.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonLeasedLock.java new file mode 100644 index 00000000..85895baf --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonLeasedLock.java @@ -0,0 +1,49 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "model for leased lock") +public class JsonLeasedLock { + long leasePeriod; + String notifyUrl; + + @ApiModelProperty(value = "Lease period") + public long getLeasePeriod() { + return leasePeriod; + } + + public void setLeasePeriod(long leasePeriod) { + this.leasePeriod = leasePeriod; + } + + @ApiModelProperty(value = "URL to be notified") + public String getNotifyUrl() { + return notifyUrl; + } + + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonOnboard.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonOnboard.java new file mode 100755 index 00000000..11311733 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonOnboard.java @@ -0,0 +1,80 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonOnboard", description = "Defines the Json for Onboarding an application.") +public class JsonOnboard { + private String appname; + private String userId; + private String password; + private String isAAF; + private String aid; + + @ApiModelProperty(value = "Application Password") + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @ApiModelProperty(value = "Application UUID") + public String getAid() { + return aid; + } + + public void setAid(String aid) { + this.aid = aid; + } + + @ApiModelProperty(value = "Application name") + public String getAppname() { + return appname; + } + + public void setAppname(String appname) { + this.appname = appname; + } + + @ApiModelProperty(value = "User Id") + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + @ApiModelProperty(value = "Is AAF Application", allowableValues = "true, false") + public String getIsAAF() { + return isAAF; + } + + public void setIsAAF(String isAAF) { + this.isAAF = isAAF; + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonSelect.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonSelect.java new file mode 100644 index 00000000..0c2d012c --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonSelect.java @@ -0,0 +1,56 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Map; + +public class JsonSelect implements Serializable { + private Map consistencyInfo; + + + public Map getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return bos.toByteArray(); + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonTable.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonTable.java new file mode 100644 index 00000000..c0d8521d --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonTable.java @@ -0,0 +1,113 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Defines the Json for Creating a new Table.") +public class JsonTable { + private String keyspaceName; + private String tableName; + + private Map fields; + private Map properties; + private String primaryKey; + private String sortingKey; + private String clusteringOrder; + private Map consistencyInfo; + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Properties") + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + @ApiModelProperty(value = "Fields") + public Map getFields() { + return fields; + } + + public void setFields(Map fields) { + this.fields = fields; + } + + @ApiModelProperty(value = "KeySpace Name") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @ApiModelProperty(value = "Table Name") + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + @ApiModelProperty(value = "Sorting Key") + public String getSortingKey() { + return sortingKey; + } + + public void setSortingKey(String sortingKey) { + this.sortingKey = sortingKey; + } + + @ApiModelProperty(value = "Clustering Order", notes = "") + public String getClusteringOrder() { + return clusteringOrder; + } + + public void setClusteringOrder(String clusteringOrder) { + this.clusteringOrder = clusteringOrder; + } + + @ApiModelProperty(value = "Primary Key") + public String getPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(String primaryKey) { + this.primaryKey = primaryKey; + } + + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/JsonUpdate.java b/src/main/java/org/onap/music/datastore/jsonobjects/JsonUpdate.java new file mode 100644 index 00000000..fae7720d --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/JsonUpdate.java @@ -0,0 +1,128 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonTable", description = "Json model for table update") +public class JsonUpdate implements Serializable { + private String keyspaceName; + private String tableName; + private Map values; + private String ttl, timestamp; + private Map consistencyInfo; + private Map conditions; + private Map row_specification; + + @ApiModelProperty(value = "Conditions") + public Map getConditions() { + return conditions; + } + + public void setConditions(Map conditions) { + this.conditions = conditions; + } + + @ApiModelProperty(value = "Information for selecting sepcific rows") + public Map getRow_specification() { + return row_specification; + } + + public void setRow_specification(Map row_specification) { + this.row_specification = row_specification; + } + + + @ApiModelProperty(value = "Keyspace name") + public String getKeyspaceName() { + return keyspaceName; + } + + public void setKeyspaceName(String keyspaceName) { + this.keyspaceName = keyspaceName; + } + + @ApiModelProperty(value = "Table name") + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + @ApiModelProperty(value = "Consistency level", allowableValues = "eventual,critical,atomic") + public Map getConsistencyInfo() { + return consistencyInfo; + } + + public void setConsistencyInfo(Map consistencyInfo) { + this.consistencyInfo = consistencyInfo; + } + + @ApiModelProperty(value = "Time to live value") + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = ttl; + } + + @ApiModelProperty(value = "Time stamp") + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + @ApiModelProperty(value = "Column values") + public Map getValues() { + return values; + } + + public void setValues(Map values) { + this.values = values; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + e.printStackTrace(); + } + return bos.toByteArray(); + } + +} diff --git a/src/main/java/org/onap/music/datastore/jsonobjects/NameSpace.java b/src/main/java/org/onap/music/datastore/jsonobjects/NameSpace.java new file mode 100644 index 00000000..232353c1 --- /dev/null +++ b/src/main/java/org/onap/music/datastore/jsonobjects/NameSpace.java @@ -0,0 +1,47 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.datastore.jsonobjects; + +import java.util.List; + + +public class NameSpace { + private String name; + private List admin; + + public List getAdmin() { + return admin; + } + + public String getName() { + return name; + } + + public void setAdmin(List admin) { + this.admin = admin; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/org/onap/music/eelf/logging/EELFLoggerDelegate.java b/src/main/java/org/onap/music/eelf/logging/EELFLoggerDelegate.java new file mode 100644 index 00000000..27ae4712 --- /dev/null +++ b/src/main/java/org/onap/music/eelf/logging/EELFLoggerDelegate.java @@ -0,0 +1,354 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.eelf.logging; + +import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN; +import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS; +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_INSTANCE_ID; +import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME; +import java.net.InetAddress; +import java.text.MessageFormat; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import javax.servlet.http.HttpServletRequest; +import org.slf4j.MDC; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.att.eelf.configuration.SLF4jWrapper; + +public class EELFLoggerDelegate extends SLF4jWrapper implements EELFLogger { + + public static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger(); + public static final EELFLogger applicationLogger = + EELFManager.getInstance().getApplicationLogger(); + public static final EELFLogger auditLogger = EELFManager.getInstance().getAuditLogger(); + public static final EELFLogger metricsLogger = EELFManager.getInstance().getMetricsLogger(); + public static final EELFLogger debugLogger = EELFManager.getInstance().getDebugLogger(); + + private String className; + private static ConcurrentMap classMap = new ConcurrentHashMap<>(); + + public EELFLoggerDelegate(final String className) { + super(className); + this.className = className; + } + + /** + * Convenience method that gets a logger for the specified class. + * + * @see #getLogger(String) + * + * @param clazz + * @return Instance of EELFLoggerDelegate + */ + public static EELFLoggerDelegate getLogger(Class clazz) { + return getLogger(clazz.getName()); + } + + /** + * Gets a logger for the specified class name. If the logger does not already exist in the map, + * this creates a new logger. + * + * @param className If null or empty, uses EELFLoggerDelegate as the class name. + * @return Instance of EELFLoggerDelegate + */ + public static EELFLoggerDelegate getLogger(final String className) { + String classNameNeverNull = className == null || "".equals(className) + ? EELFLoggerDelegate.class.getName() + : className; + EELFLoggerDelegate delegate = classMap.get(classNameNeverNull); + if (delegate == null) { + delegate = new EELFLoggerDelegate(className); + classMap.put(className, delegate); + } + return delegate; + } + + /** + * Logs a message at the lowest level: trace. + * + * @param logger + * @param msg + */ + public void trace(EELFLogger logger, String msg) { + if (logger.isTraceEnabled()) { + logger.trace(msg); + } + } + + /** + * Logs a message with parameters at the lowest level: trace. + * + * @param logger + * @param msg + * @param arguments + */ + public void trace(EELFLogger logger, String msg, Object... arguments) { + if (logger.isTraceEnabled()) { + logger.trace(msg, arguments); + } + } + + /** + * Logs a message and throwable at the lowest level: trace. + * + * @param logger + * @param msg + * @param th + */ + public void trace(EELFLogger logger, String msg, Throwable th) { + if (logger.isTraceEnabled()) { + logger.trace(msg, th); + } + } + + /** + * Logs a message at the second-lowest level: debug. + * + * @param logger + * @param msg + */ + public void debug(EELFLogger logger, String msg) { + if (logger.isDebugEnabled()) { + logger.debug(msg); + } + } + + /** + * Logs a message with parameters at the second-lowest level: debug. + * + * @param logger + * @param msg + * @param arguments + */ + public void debug(EELFLogger logger, String msg, Object... arguments) { + if (logger.isDebugEnabled()) { + logger.debug(msg, arguments); + } + } + + /** + * Logs a message and throwable at the second-lowest level: debug. + * + * @param logger + * @param msg + * @param th + */ + public void debug(EELFLogger logger, String msg, Throwable th) { + if (logger.isDebugEnabled()) { + logger.debug(msg, th); + } + } + + /** + * Logs a message at info level. + * + * @param logger + * @param msg + */ + public void info(EELFLogger logger, String msg) { + logger.info(msg); + } + + /** + * Logs a message with parameters at info level. + * + * @param logger + * @param msg + * @param arguments + */ + public void info(EELFLogger logger, String msg, Object... arguments) { + logger.info(msg, arguments); + } + + /** + * Logs a message and throwable at info level. + * + * @param logger + * @param msg + * @param th + */ + public void info(EELFLogger logger, String msg, Throwable th) { + logger.info(msg, th); + } + + /** + * Logs a message at warn level. + * + * @param logger + * @param msg + */ + public void warn(EELFLogger logger, String msg) { + logger.warn(msg); + } + + /** + * Logs a message with parameters at warn level. + * + * @param logger + * @param msg + * @param arguments + */ + public void warn(EELFLogger logger, String msg, Object... arguments) { + logger.warn(msg, arguments); + } + + /** + * Logs a message and throwable at warn level. + * + * @param logger + * @param msg + * @param th + */ + public void warn(EELFLogger logger, String msg, Throwable th) { + logger.warn(msg, th); + } + + /** + * Logs a message at error level. + * + * @param logger + * @param msg + */ + public void error(EELFLogger logger, String msg) { + logger.error(msg); + } + + /** + * Logs a message with parameters at error level. + * + * @param logger + * @param msg + * @param arguments + */ + public void error(EELFLogger logger, String msg, Object... arguments) { + logger.warn(msg, arguments); + } + + /** + * Logs a message and throwable at error level. + * + * @param logger + * @param msg + * @param th + */ + public void error(EELFLogger logger, String msg, Throwable th) { + logger.warn(msg, th); + } + + /** + * Logs a message with the associated alarm severity at error level. + * + * @param logger + * @param msg + * @param severtiy + */ + public void error(EELFLogger logger, String msg, Object /* AlarmSeverityEnum */ severtiy) { + logger.error(msg); + } + + /** + * Initializes the logger context. + */ + public void init() { + setGlobalLoggingContext(); + final String msg = + "############################ Logging is started. ############################"; + // These loggers emit the current date-time without being told. + info(applicationLogger, msg); + error(errorLogger, msg); + debug(debugLogger, msg); + info(auditLogger, msg); + info(metricsLogger, msg); + } + + /** + * Builds a message using a template string and the arguments. + * + * @param message + * @param args + * @return + */ + private String formatMessage(String message, Object... args) { + StringBuilder sbFormattedMessage = new StringBuilder(); + if (args != null && args.length > 0 && message != null && message != "") { + MessageFormat mf = new MessageFormat(message); + sbFormattedMessage.append(mf.format(args)); + } else { + sbFormattedMessage.append(message); + } + + return sbFormattedMessage.toString(); + } + + /** + * Loads all the default logging fields into the MDC context. + */ + private void setGlobalLoggingContext() { + MDC.put(MDC_SERVICE_INSTANCE_ID, ""); + try { + MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName()); + MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress()); + } catch (Exception e) { + errorLogger.error("setGlobalLoggingContext failed", e); + } + } + + public static void mdcPut(String key, String value) { + MDC.put(key, value); + } + + public static String mdcGet(String key) { + return MDC.get(key); + } + + public static void mdcRemove(String key) { + MDC.remove(key); + } + + /** + * Loads the RequestId/TransactionId into the MDC which it should be receiving with an each + * incoming REST API request. Also, configures few other request based logging fields into the + * MDC context. + * + * @param req + * @param appName + */ + public void setRequestBasedDefaultsIntoGlobalLoggingContext(HttpServletRequest req, + String appName) { + // Load the default fields + setGlobalLoggingContext(); + + // Load the request based fields + if (req != null) { + // Rest Path + MDC.put(MDC_SERVICE_NAME, req.getServletPath()); + + // Client IPAddress i.e. IPAddress of the remote host who is making + // this request. + String clientIPAddress = req.getHeader("X-FORWARDED-FOR"); + if (clientIPAddress == null) { + clientIPAddress = req.getRemoteAddr(); + } + } + } +} diff --git a/src/main/java/org/onap/music/exceptions/MusicLockingException.java b/src/main/java/org/onap/music/exceptions/MusicLockingException.java new file mode 100644 index 00000000..1a9e45d9 --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicLockingException.java @@ -0,0 +1,74 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.exceptions; + +/** + * @author inam + * + */ +public class MusicLockingException extends Exception { + + /** + * + */ + public MusicLockingException() { + + } + + /** + * @param message + */ + public MusicLockingException(String message) { + super(message); + + } + + /** + * @param cause + */ + public MusicLockingException(Throwable cause) { + super(cause); + + } + + /** + * @param message + * @param cause + */ + public MusicLockingException(String message, Throwable cause) { + super(message, cause); + + } + + /** + * @param message + * @param cause + * @param enableSuppression + * @param writableStackTrace + */ + public MusicLockingException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + + } + +} diff --git a/src/main/java/org/onap/music/exceptions/MusicPolicyVoilationException.java b/src/main/java/org/onap/music/exceptions/MusicPolicyVoilationException.java new file mode 100644 index 00000000..bade21a4 --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicPolicyVoilationException.java @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.exceptions; + +/** + * @author inam + * + */ +public class MusicPolicyVoilationException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * + */ + public MusicPolicyVoilationException() { + // TODO Auto-generated constructor stub + } + + /** + * @param message + */ + public MusicPolicyVoilationException(String message) { + super(message); + // TODO Auto-generated constructor stub + } + + /** + * @param cause + */ + public MusicPolicyVoilationException(Throwable cause) { + super(cause); + // TODO Auto-generated constructor stub + } + + /** + * @param message + * @param cause + */ + public MusicPolicyVoilationException(String message, Throwable cause) { + super(message, cause); + // TODO Auto-generated constructor stub + } + + /** + * @param message + * @param cause + * @param enableSuppression + * @param writableStackTrace + */ + public MusicPolicyVoilationException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + // TODO Auto-generated constructor stub + } + +} diff --git a/src/main/java/org/onap/music/exceptions/MusicQueryException.java b/src/main/java/org/onap/music/exceptions/MusicQueryException.java new file mode 100644 index 00000000..24b8568b --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicQueryException.java @@ -0,0 +1,89 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.exceptions; + + + +/** + * @author inam + * + */ +public class MusicQueryException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + private int errorCode; + + + /** + * + */ + public MusicQueryException() { + super(); + } + + /** + * @param message + */ + public MusicQueryException(String message) { + super(message); + } + + + + /** + * @param message + */ + public MusicQueryException(String message, int errorCode) { + super(message); + this.errorCode = errorCode; + } + + /** + * @param cause + */ + public MusicQueryException(Throwable cause) { + super(cause); + } + + /** + * @param message + * @param cause + */ + public MusicQueryException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param message + * @param cause + * @param enableSuppression + * @param writableStackTrace + */ + public MusicQueryException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/src/main/java/org/onap/music/exceptions/MusicServiceException.java b/src/main/java/org/onap/music/exceptions/MusicServiceException.java new file mode 100644 index 00000000..a3b1fc56 --- /dev/null +++ b/src/main/java/org/onap/music/exceptions/MusicServiceException.java @@ -0,0 +1,84 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ + +package org.onap.music.exceptions; + +/** + * @author inam + * + */ +public class MusicServiceException extends Exception { + + + private int errorCode; + private String errorMessage; + + public int getErrorCode() { + return errorCode; + } + + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + + public String getErrorMessage() { + return errorMessage; + } + + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + + public MusicServiceException() { + super(); + } + + + public MusicServiceException(String message) { + super(message); + + } + + + public MusicServiceException(Throwable cause) { + super(cause); + + } + + + public MusicServiceException(String message, Throwable cause) { + super(message, cause); + + } + + + public MusicServiceException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + + } + +} diff --git a/src/main/java/org/onap/music/lockingservice/LockListener.java b/src/main/java/org/onap/music/lockingservice/LockListener.java new file mode 100644 index 00000000..33188e60 --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/LockListener.java @@ -0,0 +1,39 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +/** + * This class has two methods which are call back methods when a lock is acquired and when the lock + * is released. + * + */ +public interface LockListener { + /** + * call back called when the lock is acquired + */ + public void lockAcquired(); + + /** + * call back called when the lock is released. + */ + public void lockReleased(); +} diff --git a/src/main/java/org/onap/music/lockingservice/MusicLockState.java b/src/main/java/org/onap/music/lockingservice/MusicLockState.java new file mode 100644 index 00000000..23661ad4 --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/MusicLockState.java @@ -0,0 +1,126 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import org.onap.music.main.MusicCore; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +// the state variable that will be stored in zookeeper, capturing the transitions of +public class MusicLockState implements Serializable { + public enum LockStatus { + UNLOCKED, BEING_LOCKED, LOCKED + };// captures the state of the lock + + private static EELFLogger logger = EELFManager.getInstance().getLogger(MusicLockState.class); + LockStatus lockStatus; + boolean needToSyncQuorum = false; + String lockHolder; + long leasePeriod = Long.MAX_VALUE, leaseStartTime = -1; + + public MusicLockState(LockStatus lockStatus, String lockHolder) { + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + } + + public MusicLockState(LockStatus lockStatus, String lockHolder, boolean needToSyncQuorum) { + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + this.needToSyncQuorum = needToSyncQuorum; + } + + + public long getLeasePeriod() { + return leasePeriod; + } + + public boolean isNeedToSyncQuorum() { + return needToSyncQuorum; + } + + + + public void setLeasePeriod(long leasePeriod) { + this.leasePeriod = leasePeriod; + } + + + public long getLeaseStartTime() { + return leaseStartTime; + } + + + public void setLeaseStartTime(long leaseStartTime) { + this.leaseStartTime = leaseStartTime; + } + + + + public LockStatus getLockStatus() { + return lockStatus; + } + + public void setLockStatus(LockStatus lockStatus) { + this.lockStatus = lockStatus; + } + + public String getLockHolder() { + return lockHolder; + } + + public void setLockHolder(String lockHolder) { + this.lockHolder = lockHolder; + } + + public byte[] serialize() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = null; + try { + out = new ObjectOutputStream(bos); + out.writeObject(this); + } catch (IOException e) { + logger.error(e.getMessage()); + } + return bos.toByteArray(); + } + + public static MusicLockState deSerialize(byte[] data) { + ByteArrayInputStream bis = new ByteArrayInputStream(data); + Object o = null; + ObjectInput in = null; + try { + in = new ObjectInputStream(bis); + o = in.readObject(); + } catch (ClassNotFoundException | IOException e) { + logger.error(e.getMessage()); + } + return (MusicLockState) o; + } +} diff --git a/src/main/java/org/onap/music/lockingservice/MusicLockingService.java b/src/main/java/org/onap/music/lockingservice/MusicLockingService.java new file mode 100644 index 00000000..59b502ca --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/MusicLockingService.java @@ -0,0 +1,142 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + + +import java.io.IOException; +import java.util.StringTokenizer; +import java.util.concurrent.CountDownLatch; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.Watcher.Event.KeeperState; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.apache.zookeeper.ZooKeeper; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.MusicDataStore; +import org.onap.music.eelf.logging.EELFLoggerDelegate; + +public class MusicLockingService implements Watcher { + + private static final int SESSION_TIMEOUT = 180000; + ZkStatelessLockService zkLockHandle = null; + private CountDownLatch connectedSignal = new CountDownLatch(1); + private static EELFLogger logger = + EELFManager.getInstance().getLogger(MusicLockingService.class); + // private static EELFLoggerDelegate logger = + // EELFLoggerDelegate.getLogger(MusicLockingService.class); + + public MusicLockingService() throws MusicServiceException { + try { + ZooKeeper zk = new ZooKeeper(MusicUtil.getMyZkHost(), SESSION_TIMEOUT, this); + connectedSignal.await(); + zkLockHandle = new ZkStatelessLockService(zk); + } catch (IOException e) { + logger.error(e.getMessage()); + throw new MusicServiceException("IO Error has occured" + e.getMessage()); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + throw new MusicServiceException("Exception Occured " + e.getMessage()); + } + } + + public ZkStatelessLockService getzkLockHandle() { + return zkLockHandle; + } + + public MusicLockingService(String lockServer) { + try { + ZooKeeper zk = new ZooKeeper(lockServer, SESSION_TIMEOUT, this); + connectedSignal.await(); + zkLockHandle = new ZkStatelessLockService(zk); + } catch (IOException | InterruptedException e) { + logger.error(e.getMessage()); + } + } + + public void createLockaIfItDoesNotExist(String lockName) { + if (zkLockHandle.checkIfLockExists(lockName) == false) { + String lockHolder = null; + MusicLockState ml = new MusicLockState(MusicLockState.LockStatus.UNLOCKED, lockHolder); + byte[] data = ml.serialize(); + zkLockHandle.createLock(lockName, data); + } + } + + public void setLockState(String lockName, MusicLockState mls) { + byte[] data = mls.serialize(); + zkLockHandle.setNodeData(lockName, data); + } + + public MusicLockState getLockState(String lockName) { + + byte[] data = zkLockHandle.getNodeData(lockName); + return MusicLockState.deSerialize(data); + } + + public String createLockId(String lockName) { + String lockIdWithSlash = zkLockHandle.createLockId(lockName); + return lockIdWithSlash.replace('/', '$'); + } + + public boolean isMyTurn(String lockIdWithDollar) { + String lockId = lockIdWithDollar.replace('$', '/'); + StringTokenizer st = new StringTokenizer(lockId); + String lockName = "/" + st.nextToken("/"); + try { + return zkLockHandle.lock(lockName, lockId); + } catch (KeeperException | InterruptedException e) { + logger.error(e.getMessage()); + } + return false; + } + + public void unlockAndDeleteId(String lockIdWithDollar) { + String lockId = lockIdWithDollar.replace('$', '/'); + zkLockHandle.unlock(lockId); + } + + public void deleteLock(String lockName) { + zkLockHandle.deleteLock(lockName); + } + + public String whoseTurnIsIt(String lockName) { + String lockHolder = zkLockHandle.currentLockHolder(lockName); + return lockHolder.replace('/', '$'); + + } + + public void process(WatchedEvent event) { // Watcher interface + if (event.getState() == KeeperState.SyncConnected) { + connectedSignal.countDown(); + } + } + + + public void close() { + zkLockHandle.close(); + } + +} diff --git a/src/main/java/org/onap/music/lockingservice/ProtocolSupport.java b/src/main/java/org/onap/music/lockingservice/ProtocolSupport.java new file mode 100644 index 00000000..37ae9e96 --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ProtocolSupport.java @@ -0,0 +1,205 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import org.onap.music.lockingservice.ZooKeeperOperation; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A base class for protocol implementations which provides a number of higher level helper methods + * for working with ZooKeeper along with retrying synchronous operations if the connection to + * ZooKeeper closes such as {@link #retryOperation(ZooKeeperOperation)} + * + */ +class ProtocolSupport { + private static final Logger LOG = LoggerFactory.getLogger(ProtocolSupport.class); + + protected ZooKeeper zookeeper; + private AtomicBoolean closed = new AtomicBoolean(false); + private long retryDelay = 500L; + private int retryCount = 10; + private List acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; + + // public ProtocolSupport(ZooKeeper zookeeper) { + // this.zookeeper = zookeeper; + // } + + /** + * Closes this strategy and releases any ZooKeeper resources; but keeps the ZooKeeper instance + * open + */ + public void close() { + if (closed.compareAndSet(false, true)) { + doClose(); + } + } + + /** + * return zookeeper client instance + * + * @return zookeeper client instance + */ + public ZooKeeper getZookeeper() { + return zookeeper; + } + + /** + * return the acl its using + * + * @return the acl. + */ + public List getAcl() { + return acl; + } + + /** + * set the acl + * + * @param acl the acl to set to + */ + public void setAcl(List acl) { + this.acl = acl; + } + + /** + * get the retry delay in milliseconds + * + * @return the retry delay + */ + public long getRetryDelay() { + return retryDelay; + } + + /** + * Sets the time waited between retry delays + * + * @param retryDelay the retry delay + */ + public void setRetryDelay(long retryDelay) { + this.retryDelay = retryDelay; + } + + /** + * Allow derived classes to perform some custom closing operations to release resources + */ + protected void doClose() {} + + + /** + * Perform the given operation, retrying if the connection fails + * + * @return object. it needs to be cast to the callee's expected return type. + * @param operation FILL IN + * @throws KeeperException FILL IN + * @throws InterruptedException FILL IN + */ + protected Object retryOperation(ZooKeeperOperation operation) + throws KeeperException, InterruptedException { + KeeperException exception = null; + for (int i = 0; i < retryCount; i++) { + try { + return operation.execute(); + } catch (KeeperException.SessionExpiredException e) { + LOG.warn("Session expired for: " + zookeeper + " so reconnecting due to: " + e, e); + throw e; + } catch (KeeperException.ConnectionLossException e) { + if (exception == null) { + exception = e; + } + LOG.debug("Attempt " + i + " failed with connection loss so " + + "attempting to reconnect: " + e, e); + retryDelay(i); + } + } + throw exception; + } + + /** + * Ensures that the given path exists with no data, the current ACL and no flags + * + * @param path the lock path + */ + protected void ensurePathExists(String path) { + ensureExists(path, null, acl, CreateMode.PERSISTENT); + } + + /** + * Ensures that the given path exists with the given data, ACL and flags + * + * @param path the lock path + * @param data the data + * @param acl list of ACLs applying to the path + * @param flags create mode flags + */ + protected void ensureExists(final String path, final byte[] data, final List acl, + final CreateMode flags) { + try { + retryOperation(new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + Stat stat = zookeeper.exists(path, false); + if (stat != null) { + return true; + } + zookeeper.create(path, data, acl, flags); + return true; + } + }); + } catch (KeeperException e) { + LOG.warn("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.warn("Caught: " + e, e); + } + } + + /** + * Returns true if this protocol has been closed + * + * @return true if this protocol is closed + */ + protected boolean isClosed() { + return closed.get(); + } + + /** + * Performs a retry delay if this is not the first attempt + * + * @param attemptCount the number of the attempts performed so far + */ + protected void retryDelay(int attemptCount) { + if (attemptCount > 0) { + try { + Thread.sleep(attemptCount * retryDelay); + } catch (InterruptedException e) { + LOG.debug("Failed to sleep: " + e, e); + } + } + } +} diff --git a/src/main/java/org/onap/music/lockingservice/ZNodeName.java b/src/main/java/org/onap/music/lockingservice/ZNodeName.java new file mode 100644 index 00000000..c8d14cba --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ZNodeName.java @@ -0,0 +1,117 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +/** + * Represents an ephemeral znode name which has an ordered sequence number and can be sorted in + * order + * + */ +class ZNodeName implements Comparable { + private final String name; + private String prefix; + private int sequence = -1; + private static EELFLogger LOG = EELFManager.getInstance().getLogger(ZNodeName.class); + + public ZNodeName(String name) { + if (name == null) { + throw new NullPointerException("id cannot be null"); + } + this.name = name; + this.prefix = name; + int idx = name.lastIndexOf('-'); + if (idx >= 0) { + this.prefix = name.substring(0, idx); + try { + this.sequence = Integer.parseInt(name.substring(idx + 1)); + // If an exception occurred we misdetected a sequence suffix, + // so return -1. + } catch (NumberFormatException e) { + LOG.info("Number format exception for " + idx, e); + } catch (ArrayIndexOutOfBoundsException e) { + LOG.info("Array out of bounds for " + idx, e); + } + } + } + + @Override + public String toString() { + return name.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + ZNodeName sequence = (ZNodeName) o; + + if (!name.equals(sequence.name)) + return false; + + return true; + } + + @Override + public int hashCode() { + return name.hashCode() + 37; + } + + public int compareTo(ZNodeName that) { + int answer = this.prefix.compareTo(that.prefix); + if (answer == 0) { + int s1 = this.sequence; + int s2 = that.sequence; + if (s1 == -1 && s2 == -1) { + return this.name.compareTo(that.name); + } + answer = s1 == -1 ? 1 : s2 == -1 ? -1 : s1 - s2; + } + return answer; + } + + /** + * Returns the name of the znode + */ + public String getName() { + return name; + } + + /** + * Returns the sequence number + */ + public int getZNodeName() { + return sequence; + } + + /** + * Returns the text prefix before the sequence number + */ + public String getPrefix() { + return prefix; + } +} diff --git a/src/main/java/org/onap/music/lockingservice/ZkStatelessLockService.java b/src/main/java/org/onap/music/lockingservice/ZkStatelessLockService.java new file mode 100644 index 00000000..99ccd4db --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ZkStatelessLockService.java @@ -0,0 +1,334 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +/** + * A protocol to implement an exclusive write lock or to elect a leader. + *

+ * You invoke {@link #lock()} to start the process of grabbing the lock; you may get the lock then + * or it may be some time later. + *

+ * You can register a listener so that you are invoked when you get the lock; otherwise you can ask + * if you have the lock by calling {@link #isOwner()} + * + */ +public class ZkStatelessLockService extends ProtocolSupport { + public ZkStatelessLockService(ZooKeeper zk) { + zookeeper = zk; + } + + private static EELFLogger LOG = + EELFManager.getInstance().getLogger(ZkStatelessLockService.class); + + protected void createLock(final String path, final byte[] data) { + final List acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; + try { + retryOperation(new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + zookeeper.create(path, data, acl, CreateMode.PERSISTENT); + return true; + } + }); + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + } + } + + public void close() { + try { + zookeeper.close(); + } catch (InterruptedException e) { + LOG.info(e.getMessage()); + } + } + + public void setNodeData(final String lockName, final byte[] data) { + try { + retryOperation(new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + zookeeper.getSessionId(); + zookeeper.setData("/" + lockName, data, -1); + return true; + } + }); + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + } + + } + + public byte[] getNodeData(final String lockName) { + try { + if (zookeeper.exists("/" + lockName, null) != null) + return zookeeper.getData("/" + lockName, false, null); + else + return null; + + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + } + return null; + } + + public boolean checkIfLockExists(String lockName) { + boolean result = false; + try { + Stat stat = zookeeper.exists(lockName, false); + if (stat != null) { + result = true; + } + } catch (KeeperException e) { + LOG.info(e.getMessage()); + } catch (InterruptedException e) { + LOG.info(e.getMessage()); + } + return result; + } + + public void createNode(String nodeName) { + ensurePathExists(nodeName); + } + + public String createLockId(String dir) { + ensurePathExists(dir); + LockZooKeeperOperation zop = new LockZooKeeperOperation(dir); + + try { + retryOperation(zop); + } catch (KeeperException e) { + LOG.info(e.getMessage()); + } catch (InterruptedException e) { + LOG.info(e.getMessage()); + } + return zop.getId(); + } + + /** + * Attempts to acquire the exclusive write lock returning whether or not it was acquired. Note + * that the exclusive lock may be acquired some time later after this method has been invoked + * due to the current lock owner going away. + */ + public synchronized boolean lock(String dir, String lockId) + throws KeeperException, InterruptedException { + if (isClosed()) { + return false; + } + LockZooKeeperOperation zop = new LockZooKeeperOperation(dir, lockId); + return (Boolean) retryOperation(zop); + } + + /** + * Removes the lock or associated znode if you no longer require the lock. this also removes + * your request in the queue for locking in case you do not already hold the lock. + * + * @throws RuntimeException throws a runtime exception if it cannot connect to zookeeper. + */ + public synchronized void unlock(String lockId) throws RuntimeException { + final String id = lockId; + if (!isClosed() && id != null) { + try { + ZooKeeperOperation zopdel = new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + zookeeper.delete(id, -1); + return Boolean.TRUE; + } + }; + zopdel.execute(); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + // set that we have been interrupted. + Thread.currentThread().interrupt(); + } catch (KeeperException.NoNodeException e) { + // do nothing + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e); + } + } + } + + public synchronized String currentLockHolder(String mainLock) { + final String id = mainLock; + if (!isClosed() && id != null) { + List names; + try { + names = zookeeper.getChildren(id, false); + if (names.isEmpty()) + return ""; + SortedSet sortedNames = new TreeSet(); + for (String name : names) { + sortedNames.add(new ZNodeName(id + "/" + name)); + } + return sortedNames.first().getName(); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + // set that we have been interrupted. + Thread.currentThread().interrupt(); + } catch (KeeperException.NoNodeException e) { + // do nothing + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e); + } + } + return "No lock holder!"; + } + + public synchronized void deleteLock(String mainLock) { + final String id = mainLock; + if (!isClosed() && id != null) { + try { + ZooKeeperOperation zopdel = new ZooKeeperOperation() { + public boolean execute() throws KeeperException, InterruptedException { + List names = zookeeper.getChildren(id, false); + for (String name : names) { + zookeeper.delete(id + "/" + name, -1); + } + zookeeper.delete(id, -1); + return Boolean.TRUE; + } + }; + zopdel.execute(); + } catch (InterruptedException e) { + LOG.info("Caught: " + e, e); + // set that we have been interrupted. + Thread.currentThread().interrupt(); + } catch (KeeperException.NoNodeException e) { + // do nothing + } catch (KeeperException e) { + LOG.info("Caught: " + e, e); + throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e); + } + } + + } + + /** + * a zoookeeper operation that is mainly responsible for all the magic required for locking. + */ + private class LockZooKeeperOperation implements ZooKeeperOperation { + + /** + * find if we have been created earlier if not create our node + * + * @param prefix the prefix node + * @param zookeeper the zookeeper client + * @param dir the dir parent + * @throws KeeperException + * @throws InterruptedException + */ + private String dir; + private String id = null; + + public String getId() { + return id; + } + + public LockZooKeeperOperation(String dir) { + this.dir = dir; + } + + public LockZooKeeperOperation(String dir, String id) { + this.dir = dir; + this.id = id; + } + + /** + * the command that is run and retried for actually obtaining the lock + * + * @return if the command was successful or not + */ + public boolean execute() throws KeeperException, InterruptedException { + do { + if (id == null) { + String prefix = "x-"; + byte[] data = {0x12, 0x34}; + id = zookeeper.create(dir + "/" + prefix, data, getAcl(), + CreateMode.PERSISTENT_SEQUENTIAL); + + if (LOG.isDebugEnabled()) { + LOG.debug("Created id: " + id); + } + if (id != null) + break; + } + if (id != null) { + List names = zookeeper.getChildren(dir, false); + if (names.isEmpty()) { + LOG.info("No children in: " + dir + " when we've just " + + "created one! Lets recreate it..."); + // lets force the recreation of the id + id = null; + } else { + // lets sort them explicitly (though they do seem to come back in order + // ususally :) + ZNodeName idName = new ZNodeName(id); + SortedSet sortedNames = new TreeSet(); + for (String name : names) { + sortedNames.add(new ZNodeName(dir + "/" + name)); + } + if (!sortedNames.contains(idName)) + return Boolean.FALSE; + + SortedSet lessThanMe = sortedNames.headSet(idName); + if (!lessThanMe.isEmpty()) { + ZNodeName lastChildName = lessThanMe.last(); + String lastChildId = lastChildName.getName(); + if (LOG.isDebugEnabled()) { + LOG.debug("watching less than me node: " + lastChildId); + } + Stat stat = zookeeper.exists(lastChildId, false); + if (stat != null) { + return Boolean.FALSE; + } else { + LOG.info("Could not find the" + " stats for less than me: " + + lastChildName.getName()); + } + } else + return Boolean.TRUE; + } + } + } while (id == null); + return Boolean.FALSE; + } + } + +} + diff --git a/src/main/java/org/onap/music/lockingservice/ZooKeeperOperation.java b/src/main/java/org/onap/music/lockingservice/ZooKeeperOperation.java new file mode 100644 index 00000000..7020d14d --- /dev/null +++ b/src/main/java/org/onap/music/lockingservice/ZooKeeperOperation.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.lockingservice; + +import org.apache.zookeeper.KeeperException; + +/** + * A callback object which can be used for implementing retry-able operations in the + * {@link org.onap.music.lockingservice.ProtocolSupport} class + * + */ +public interface ZooKeeperOperation { + + /** + * Performs the operation - which may be involved multiple times if the connection + * to ZooKeeper closes during this operation + * + * @return the result of the operation or null + * @throws KeeperException FILL IN + * @throws InterruptedException FILL IN + */ + public boolean execute() throws KeeperException, InterruptedException; +} diff --git a/src/main/java/org/onap/music/main/CachingUtil.java b/src/main/java/org/onap/music/main/CachingUtil.java new file mode 100755 index 00000000..0ab055df --- /dev/null +++ b/src/main/java/org/onap/music/main/CachingUtil.java @@ -0,0 +1,370 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.jcs.JCS; +import org.apache.commons.jcs.access.CacheAccess; +import org.apache.log4j.Logger; +import org.codehaus.jackson.map.ObjectMapper; +import org.onap.music.datastore.jsonobjects.AAFResponse; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; + +/** + * All Caching related logic is handled by this class and a schedule cron runs to update cache. + * + * @author Vikram + * + */ +public class CachingUtil implements Runnable { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(CachingUtil.class); + + private static CacheAccess musicCache = JCS.getInstance("musicCache"); + private static CacheAccess> aafCache = JCS.getInstance("aafCache"); + private static CacheAccess appNameCache = JCS.getInstance("appNameCache"); + private static Map userAttempts = new HashMap<>(); + private static Map lastFailedTime = new HashMap<>(); + + public boolean isCacheRefreshNeeded() { + if (aafCache.get("initBlankMap") == null) + return true; + return false; + } + + public void initializeMusicCache() { + logger.info("Initializing Music Cache..."); + musicCache.put("isInitialized", "true"); + } + + public void initializeAafCache() { + logger.info("Resetting and initializing AAF Cache..."); + + // aafCache.clear(); + // loop through aafCache ns .. only the authenticated ns will be re cached. and non + // authenticated will wait for user to retry. + String query = "SELECT application_name, keyspace_name, username, password FROM admin.keyspace_master WHERE is_api = ? allow filtering"; + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString(query); + try { + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), false)); + } catch (Exception e1) { + e1.printStackTrace(); + logger.error("Exception is " + e1.getMessage() + "during initalizeAafCache"); + } + ResultSet rs = MusicCore.get(pQuery); + Iterator it = rs.iterator(); + Map map = null; + while (it.hasNext()) { + Row row = it.next(); + String nameSpace = row.getString("keyspace_name"); + String userId = row.getString("username"); + String password = row.getString("password"); + String keySpace = row.getString("application_name"); + try { + userAttempts.put(nameSpace, 0); + AAFResponse responseObj = triggerAAF(nameSpace, userId, password); + if (responseObj.getNs().size() > 0) { + map = new HashMap<>(); + map.put(userId, password); + aafCache.put(nameSpace, map); + musicCache.put(nameSpace, keySpace); + logger.debug("Cronjob: Cache Updated with AAF response for namespace " + + nameSpace); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + logger.error("Something at AAF was changed for ns: " + nameSpace + + ". So not updating Cache for the namespace. "); + logger.error("Exception is " + e.getMessage()); + } + } + + } + + @Override + public void run() { + logger.debug("Scheduled task invoked. Refreshing Cache..."); + initializeAafCache(); + } + + public static boolean authenticateAAFUser(String nameSpace, String userId, String password, + String keySpace) throws Exception { + + if (aafCache.get(nameSpace) != null) { + if (!musicCache.get(nameSpace).equals(keySpace)) { + logger.debug("Create new application for the same namespace."); + } else if (aafCache.get(nameSpace).get(userId).equals(password)) { + logger.debug("Authenticated with cache value.."); + // reset invalid attempts to 0 + userAttempts.put(nameSpace, 0); + return true; + } else { + // call AAF update cache with new password + if (userAttempts.get(nameSpace) == null) + userAttempts.put(nameSpace, 0); + if ((Integer) userAttempts.get(nameSpace) >= 3) { + logger.info("Reached max attempts. Checking if time out.."); + logger.info("Failed time: " + lastFailedTime.get(nameSpace).getTime()); + Calendar calendar = Calendar.getInstance(); + long delayTime = (calendar.getTimeInMillis() + - lastFailedTime.get(nameSpace).getTimeInMillis()); + logger.info("Delayed time: " + delayTime); + if (delayTime > 120000) { + logger.info("Resetting failed attempt."); + userAttempts.put(nameSpace, 0); + } else { + throw new Exception( + "No more attempts allowed. Please wait for atleast 2 min."); + } + } + logger.error("Cache not authenticated.."); + logger.info("Check AAF again..."); + } + } + + AAFResponse responseObj = triggerAAF(nameSpace, userId, password); + if (responseObj.getNs().size() > 0) { + if (responseObj.getNs().get(0).getAdmin().contains(userId)) + return true; + + } + logger.info("Invalid user. Cache not updated"); + return false; + } + + private static AAFResponse triggerAAF(String nameSpace, String userId, String password) + throws Exception { + if (MusicUtil.getAafEndpointUrl() == null) { + throw new Exception("AAF endpoint is not set. Please specify in the properties file."); + } + Client client = Client.create(); + // WebResource webResource = + // client.resource("https://aaftest.test.att.com:8095/proxy/authz/nss/"+nameSpace); + WebResource webResource = client.resource(MusicUtil.getAafEndpointUrl().concat(nameSpace)); + String plainCreds = userId + ":" + password; + byte[] plainCredsBytes = plainCreds.getBytes(); + byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes); + String base64Creds = new String(base64CredsBytes); + + ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON) + .header("Authorization", "Basic " + base64Creds) + .header("content-type", "application/json").get(ClientResponse.class); + if (response.getStatus() != 200) { + if (userAttempts.get(nameSpace) == null) + userAttempts.put(nameSpace, 0); + if ((Integer) userAttempts.get(nameSpace) >= 2) { + lastFailedTime.put(nameSpace, Calendar.getInstance()); + userAttempts.put(nameSpace, ((Integer) userAttempts.get(nameSpace) + 1)); + throw new Exception( + "Reached max invalid attempts. Please contact admin and retry with valid credentials."); + } + userAttempts.put(nameSpace, ((Integer) userAttempts.get(nameSpace) + 1)); + throw new Exception( + "Unable to authenticate. Please check the AAF credentials against namespace."); + // TODO Allow for 2-3 times and forbid any attempt to trigger AAF with invalid values + // for specific time. + } + response.getHeaders().put(HttpHeaders.CONTENT_TYPE, + Arrays.asList(MediaType.APPLICATION_JSON)); + // AAFResponse output = response.getEntity(AAFResponse.class); + response.bufferEntity(); + String x = response.getEntity(String.class); + AAFResponse responseObj = new ObjectMapper().readValue(x, AAFResponse.class); + return responseObj; + } + + public static Map authenticateAIDUser(String aid, String keyspace) + throws Exception { + Map resultMap = new HashMap<>(); + String uuid = null; + /* + * if(aid == null || aid.length() == 0) { resultMap.put("Exception Message", + * "AID is missing for the keyspace requested."); //create a new AID ?? } else + */ + if (musicCache.get(keyspace) == null) { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT uuid from admin.keyspace_master where keyspace_name = '" + + keyspace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + uuid = rs.getUUID("uuid").toString(); + musicCache.put(keyspace, uuid); + } catch (Exception e) { + String msg = e.getMessage(); + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + resultMap.put("Exception", "Unauthorized operation. Check AID and Keyspace. " + + "Exception from MUSIC is: " + + (msg == null ? "Keyspace is new so no AID should be passed in Header." + : msg)); + return resultMap; + } + if (!musicCache.get(keyspace).toString().equals(aid)) { + resultMap.put("Exception Message", + "Unauthorized operation. Invalid AID for the keyspace"); + return resultMap; + } + } else if (musicCache.get(keyspace) != null + && !musicCache.get(keyspace).toString().equals(aid)) { + resultMap.put("Exception Message", + "Unauthorized operation. Invalid AID for the keyspace"); + return resultMap; + } + resultMap.put("aid", uuid); + return resultMap; + } + + public static void updateMusicCache(String aid, String keyspace) { + logger.info("Updating musicCache for keyspace " + keyspace + " with aid " + aid); + musicCache.put(keyspace, aid); + } + + public static void updateisAAFCache(String namespace, String isAAF) { + appNameCache.put(namespace, isAAF); + } + + public static Boolean isAAFApplication(String namespace) { + + String isAAF = appNameCache.get(namespace); + if (isAAF == null) { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT is_aaf from admin.keyspace_master where application_name = '" + + namespace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + isAAF = String.valueOf(rs.getBool("is_aaf")); + appNameCache.put(namespace, isAAF); + } catch (Exception e) { + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + e.printStackTrace(); + } + } + return Boolean.valueOf(isAAF); + } + + public static String getUuidFromMusicCache(String keyspace) { + String uuid = musicCache.get(keyspace); + if (uuid == null) { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT uuid from admin.keyspace_master where keyspace_name = '" + + keyspace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + uuid = rs.getUUID("uuid").toString(); + musicCache.put(keyspace, uuid); + } catch (Exception e) { + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + e.printStackTrace(); + } + } + return uuid; + } + + public static String getAppName(String keyspace) { + String appName = null; + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT application_name from admin.keyspace_master where keyspace_name = '" + + keyspace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + appName = rs.getString("application_name"); + } catch (Exception e) { + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + e.printStackTrace(); + } + return appName; + } + + public static String generateUUID() { + String uuid = UUID.randomUUID().toString(); + logger.info("New AID generated: " + uuid); + return uuid; + } + + public static Map validateRequest(String nameSpace, String userId, + String password, String keyspace, String aid, String operation) { + Map resultMap = new HashMap<>(); + if (!"createKeySpace".equals(operation)) { + if (nameSpace == null) { + resultMap.put("Exception", "Application namespace is mandatory."); + } + } + return resultMap; + + } + + public static Map verifyOnboarding(String ns, String userId, String password) + throws Exception { + Map resultMap = new HashMap<>(); + if (ns == null || userId == null || password == null) { + logger.error("One or more required headers is missing. userId: " + userId + + " :: password: " + password); + resultMap.put("Exception", + "One or more required headers appName(ns), userId, password is missing. Please check."); + return resultMap; + } + PreparedQueryObject queryObject = new PreparedQueryObject(); + queryObject.appendQueryString( + "select * from admin.keyspace_master where application_name=? and username=? allow filtering"); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), ns)); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + Row rs = MusicCore.get(queryObject).one(); + if (rs == null) { + logger.error("Namespace and UserId doesn't match. namespace: " + ns + " and userId: " + + userId); + resultMap.put("Exception", "Application " + ns + + " doesn't seem to be Onboarded. Please onboard your application with MUSIC. If already onboarded contact Admin"); + } else { + boolean is_aaf = rs.getBool("is_aaf"); + String keyspace = rs.getString("keyspace_name"); + if (!is_aaf) { + if (!keyspace.equals(MusicUtil.DEFAULTKEYSPACENAME)) { + logger.error("Non AAF applications are allowed to have only one keyspace per application."); + resultMap.put("Exception", + "Non AAF applications are allowed to have only one keyspace per application."); + } + } + } + return resultMap; + } +} diff --git a/src/main/java/org/onap/music/main/CronJobManager.java b/src/main/java/org/onap/music/main/CronJobManager.java new file mode 100644 index 00000000..fb4a2ac3 --- /dev/null +++ b/src/main/java/org/onap/music/main/CronJobManager.java @@ -0,0 +1,47 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; + +@WebListener +public class CronJobManager implements ServletContextListener { + + private ScheduledExecutorService scheduler; + + @Override + public void contextInitialized(ServletContextEvent event) { + scheduler = Executors.newSingleThreadScheduledExecutor(); + scheduler.scheduleAtFixedRate(new CachingUtil(), 0, 24, TimeUnit.HOURS); + } + + @Override + public void contextDestroyed(ServletContextEvent event) { + scheduler.shutdownNow(); + } + +} diff --git a/src/main/java/org/onap/music/main/MusicCore.java b/src/main/java/org/onap/music/main/MusicCore.java new file mode 100644 index 00000000..592bae92 --- /dev/null +++ b/src/main/java/org/onap/music/main/MusicCore.java @@ -0,0 +1,874 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + + +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; +import org.onap.music.datastore.MusicDataStore; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +// import org.onap.music.eelf.logging.EELFLoggerDelegate; +import org.onap.music.exceptions.MusicLockingException; +import org.onap.music.exceptions.MusicQueryException; +import org.onap.music.exceptions.MusicServiceException; +import org.onap.music.lockingservice.MusicLockState; +import org.onap.music.lockingservice.MusicLockState.LockStatus; +import org.onap.music.lockingservice.MusicLockingService; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.ColumnDefinitions.Definition; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.TableMetadata; + +/** + * This class ..... + * + * + */ +public class MusicCore { + + public static MusicLockingService mLockHandle = null; + public static MusicDataStore mDstoreHandle = null; + private static EELFLogger logger = EELFManager.getInstance().getLogger(MusicCore.class); + + public static class Condition { + Map conditions; + String selectQueryForTheRow; + + public Condition(Map conditions, String selectQueryForTheRow) { + this.conditions = conditions; + this.selectQueryForTheRow = selectQueryForTheRow; + } + + public boolean testCondition() { + // first generate the row + PreparedQueryObject query = new PreparedQueryObject(); + query.appendQueryString(selectQueryForTheRow); + ResultSet results = quorumGet(query); + Row row = results.one(); + return getDSHandle().doesRowSatisfyCondition(row, conditions); + } + } + + + public static MusicLockingService getLockingServiceHandle() throws MusicLockingException { + logger.info("Acquiring lock store handle"); + long start = System.currentTimeMillis(); + + if (mLockHandle == null) { + try { + mLockHandle = new MusicLockingService(); + } catch (Exception e) { + logger.error("Failed to aquire Locl store handle" + e.getMessage()); + throw new MusicLockingException("Failed to aquire Locl store handle " + e); + } + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire lock store handle:" + (end - start) + " ms"); + return mLockHandle; + } + + /** + * + * @param remoteIp + * @return + */ + public static MusicDataStore getDSHandle(String remoteIp) { + logger.info("Acquiring data store handle"); + long start = System.currentTimeMillis(); + if (mDstoreHandle == null) { + mDstoreHandle = new MusicDataStore(remoteIp); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire data store handle:" + (end - start) + " ms"); + return mDstoreHandle; + } + + /** + * + * @return + */ + public static MusicDataStore getDSHandle() { + logger.info("Acquiring data store handle"); + long start = System.currentTimeMillis(); + if (mDstoreHandle == null) { + mDstoreHandle = new MusicDataStore(); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire data store handle:" + (end - start) + " ms"); + return mDstoreHandle; + } + + public static String createLockReference(String lockName) { + logger.info("Creating lock reference for lock name:" + lockName); + long start = System.currentTimeMillis(); + String lockId = null; + try { + lockId = getLockingServiceHandle().createLockId("/" + lockName); + } catch (MusicLockingException e) { + logger.error("Failed to create Lock Reference " + lockName); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to create lock reference:" + (end - start) + " ms"); + return lockId; + } + + /** + * + * @param key + * @return + */ + public static boolean isTableOrKeySpaceLock(String key) { + String[] splitString = key.split("\\."); + if (splitString.length > 2) + return false; + else + return true; + } + + /** + * + * @param key + * @return + */ + public static MusicLockState getMusicLockState(String key) { + long start = System.currentTimeMillis(); + try { + String[] splitString = key.split("\\."); + String keyspaceName = splitString[0]; + String tableName = splitString[1]; + String primaryKey = splitString[2]; + MusicLockState mls; + String lockName = keyspaceName + "." + tableName + "." + primaryKey; + mls = getLockingServiceHandle().getLockState(lockName); + long end = System.currentTimeMillis(); + logger.info("Time taken to get lock state:" + (end - start) + " ms"); + return mls; + } catch (NullPointerException | MusicLockingException e) { + logger.error("No lock object exists as of now.." + e); + } + return null; + } + + public static ReturnType acquireLockWithLease(String key, String lockId, long leasePeriod) { + try { + long start = System.currentTimeMillis(); + /* check if the current lock has exceeded its lease and if yes, release that lock */ + MusicLockState mls = getMusicLockState(key); + if (mls != null) { + if (mls.getLockStatus().equals(LockStatus.LOCKED)) { + logger.info("The current lock holder for " + key + " is " + mls.getLockHolder() + + ". Checking if it has exceeded lease"); + long currentLockPeriod = System.currentTimeMillis() - mls.getLeaseStartTime(); + long currentLeasePeriod = mls.getLeasePeriod(); + if (currentLockPeriod > currentLeasePeriod) { + logger.info("Lock period " + currentLockPeriod + + " has exceeded lease period " + currentLeasePeriod); + boolean voluntaryRelease = false; + String currentLockHolder = mls.getLockHolder(); + mls = releaseLock(currentLockHolder, voluntaryRelease); + } + } + } else + logger.debug("There is no lock state object for " + key); + + /* + * call the traditional acquire lock now and if the result returned is true, set the + * begin time-stamp and lease period + */ + if (acquireLock(key, lockId) == true) { + mls = getMusicLockState(key);// get latest state + if (mls.getLeaseStartTime() == -1) {// set it again only if it is not set already + mls.setLeaseStartTime(System.currentTimeMillis()); + mls.setLeasePeriod(leasePeriod); + getLockingServiceHandle().setLockState(key, mls); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to acquire leased lock:" + (end - start) + " ms"); + return new ReturnType(ResultType.SUCCESS, "Accquired lock"); + } else { + long end = System.currentTimeMillis(); + logger.info("Time taken to fail to acquire leased lock:" + (end - start) + " ms"); + return new ReturnType(ResultType.FAILURE, "Could not acquire lock"); + } + } catch (Exception e) { + StringWriter sw = new StringWriter(); + logger.error(e.getMessage()); + String exceptionAsString = sw.toString(); + return new ReturnType(ResultType.FAILURE, + "Exception thrown in acquireLockWithLease:\n" + exceptionAsString); + } + } + + public static boolean acquireLock(String key, String lockId) { + /* + * first check if I am on top. Since ids are not reusable there is no need to check + * lockStatus If the status is unlocked, then the above call will automatically return + * false. + */ + Boolean result = false; + try { + result = getLockingServiceHandle().isMyTurn(lockId); + } catch (MusicLockingException e2) { + logger.error("Failed to aquireLock lockId " + lockId + " " + e2); + } + if (result == false) { + logger.info("In acquire lock: Not your turn, someone else has the lock"); + return false; + } + + + // this is for backward compatibility where locks could also be acquired on just + // keyspaces or tables. + if (isTableOrKeySpaceLock(key) == true) { + logger.info("In acquire lock: A table or keyspace lock so no need to perform sync...so returning true"); + return true; + } + + // read the lock name corresponding to the key and if the status is locked or being locked, + // then return false + MusicLockState currentMls = null; + MusicLockState newMls = null; + try { + currentMls = getMusicLockState(key); + String currentLockHolder = currentMls.getLockHolder(); + if (lockId.equals(currentLockHolder)) { + logger.info("In acquire lock: You already have the lock!"); + return true; + } + } catch (NullPointerException e) { + logger.error("In acquire lock:No one has tried to acquire the lock yet.."); + } + + // change status to "being locked". This state transition is necessary to ensure syncing + // before granting the lock + String lockHolder = null; + boolean needToSyncQuorum = false; + if (currentMls != null) + needToSyncQuorum = currentMls.isNeedToSyncQuorum(); + + + newMls = new MusicLockState(MusicLockState.LockStatus.BEING_LOCKED, lockHolder, + needToSyncQuorum); + try { + getLockingServiceHandle().setLockState(key, newMls); + } catch (MusicLockingException e1) { + logger.error("Failed to set Lock state " + key + " " + e1); + } + logger.info("In acquire lock: Set lock state to being_locked"); + + // do syncing if this was a forced lock release + if (needToSyncQuorum) { + logger.info("In acquire lock: Since there was a forcible release, need to sync quorum!"); + syncQuorum(key); + } + + // change status to locked + lockHolder = lockId; + needToSyncQuorum = false; + newMls = new MusicLockState(MusicLockState.LockStatus.LOCKED, lockHolder, needToSyncQuorum); + try { + getLockingServiceHandle().setLockState(key, newMls); + } catch (MusicLockingException e) { + logger.error("Failed to set Lock state " + key + " " + e); + } + logger.info("In acquire lock: Set lock state to locked and assigned current lock ref " + + lockId + " as holder"); + return result; + } + + + + /** + * + * @param keyspaceName + * @param kspObject + * @return + * @throws Exception + */ + public boolean createKeyspace(String keyspaceName, JsonKeySpace kspObject) throws Exception { + return true; + } + + + private static void syncQuorum(String key) { + logger.info("Performing sync operation---"); + String[] splitString = key.split("\\."); + String keyspaceName = splitString[0]; + String tableName = splitString[1]; + String primaryKeyValue = splitString[2]; + PreparedQueryObject selectQuery = new PreparedQueryObject(); + PreparedQueryObject updateQuery = new PreparedQueryObject(); + + // get the primary key d + TableMetadata tableInfo = returnColumnMetadata(keyspaceName, tableName); + String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName();// we only support single + // primary key + DataType primaryKeyType = tableInfo.getPrimaryKey().get(0).getType(); + String cqlFormattedPrimaryKeyValue = + MusicUtil.convertToCQLDataType(primaryKeyType, primaryKeyValue); + + // get the row of data from a quorum + selectQuery.appendQueryString("SELECT * FROM " + keyspaceName + "." + tableName + " WHERE " + + primaryKeyName + "= ?" + ";"); + selectQuery.addValue(cqlFormattedPrimaryKeyValue); + // String selectQuery = "SELECT * FROM "+keyspaceName+"."+tableName+ " WHERE + // "+primaryKeyName+"="+cqlFormattedPrimaryKeyValue+";"; + ResultSet results = null; + try { + results = getDSHandle().executeCriticalGet(selectQuery); + // write it back to a quorum + Row row = results.one(); + ColumnDefinitions colInfo = row.getColumnDefinitions(); + int totalColumns = colInfo.size(); + int counter = 1; + // String fieldValueString=""; + StringBuilder fieldValueString = new StringBuilder(""); + for (Definition definition : colInfo) { + String colName = definition.getName(); + if (colName.equals(primaryKeyName)) + continue; + DataType colType = definition.getType(); + Object valueObj = getDSHandle().getColValue(row, colName, colType); + String valueString = MusicUtil.convertToCQLDataType(colType, valueObj); + // fieldValueString = fieldValueString+ colName+"="+valueString; + fieldValueString.append(colName + " = ?"); + updateQuery.addValue(valueString); + if (counter != (totalColumns - 1)) + fieldValueString.append(","); + counter = counter + 1; + } + updateQuery.appendQueryString("UPDATE " + keyspaceName + "." + tableName + " SET " + + fieldValueString + " WHERE " + primaryKeyName + "= ? " + ";"); + updateQuery.addValue(cqlFormattedPrimaryKeyValue); + // String updateQuery = "UPDATE "+keyspaceName+"."+tableName+" SET "+fieldValueString+" + // WHERE "+primaryKeyName+"="+cqlFormattedPrimaryKeyValue+";"; + + getDSHandle().executePut(updateQuery, "critical"); + } catch (MusicServiceException | MusicQueryException e) { + logger.error("Failed to execute update query " + updateQuery + " " + e); + } + } + + + /** + * this function is mainly for the benchmarks to see the effect of lock deletion. + * + * @param keyspaceName + * @param tableName + * @param primaryKey + * @param queryObject + * @param conditionInfo + * @return + */ + public static ReturnType atomicPutWithDeleteLock(String keyspaceName, String tableName, + String primaryKey, PreparedQueryObject queryObject, Condition conditionInfo) { + long start = System.currentTimeMillis(); + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long lockCreationTime = System.currentTimeMillis(); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = acquireLockWithLease(key, lockId, leasePeriod); + long lockAcqTime = System.currentTimeMillis(); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + ReturnType criticalPutResult = criticalPut(keyspaceName, tableName, primaryKey, + queryObject, lockId, conditionInfo); + long criticalPutTime = System.currentTimeMillis(); + deleteLock(key); + long lockDeleteTime = System.currentTimeMillis(); + String timingInfo = "|lock creation time:" + (lockCreationTime - start) + + "|lock accquire time:" + (lockAcqTime - lockCreationTime) + + "|critical put time:" + (criticalPutTime - lockAcqTime) + + "|lock delete time:" + (lockDeleteTime - criticalPutTime) + "|"; + criticalPutResult.setTimingInfo(timingInfo); + return criticalPutResult; + } else { + logger.info("unable to acquire lock, id " + lockId); + deleteLock(key); + return lockAcqResult; + } + } + + /** + * + * @param query + * @return ResultSet + */ + public static ResultSet quorumGet(PreparedQueryObject query) { + ResultSet results = null; + try { + results = getDSHandle().executeCriticalGet(query); + } catch (MusicServiceException | MusicQueryException e) { + logger.error(e.getMessage()); + } + return results; + + } + + /** + * + * @param results + * @return + */ + public static Map> marshallResults(ResultSet results) { + return getDSHandle().marshalData(results); + } + + /** + * + * @param lockName + * @return + */ + public static String whoseTurnIsIt(String lockName) { + + try { + return getLockingServiceHandle().whoseTurnIsIt("/" + lockName) + ""; + } catch (MusicLockingException e) { + logger.error("Failed whoseTurnIsIt " + lockName + " " + e); + } + return null; + + + } + + /** + * + * @param lockId + * @return + */ + public static String getLockNameFromId(String lockId) { + StringTokenizer st = new StringTokenizer(lockId); + return st.nextToken("$"); + } + + public static void destroyLockRef(String lockId) { + long start = System.currentTimeMillis(); + try { + getLockingServiceHandle().unlockAndDeleteId(lockId); + } catch (MusicLockingException e) { + logger.error("Failed to Destroy Lock Ref " + lockId + " " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to destroy lock reference:" + (end - start) + " ms"); + } + + public static MusicLockState releaseLock(String lockId, boolean voluntaryRelease) { + long start = System.currentTimeMillis(); + try { + getLockingServiceHandle().unlockAndDeleteId(lockId); + } catch (MusicLockingException e1) { + logger.error("Failed to release Lock " + lockId + " " + e1); + } + String lockName = getLockNameFromId(lockId); + MusicLockState mls; + String lockHolder = null; + if (voluntaryRelease) { + mls = new MusicLockState(MusicLockState.LockStatus.UNLOCKED, lockHolder); + logger.info("In unlock: lock voluntarily released for " + lockId); + } else { + boolean needToSyncQuorum = true; + mls = new MusicLockState(MusicLockState.LockStatus.UNLOCKED, lockHolder, + needToSyncQuorum); + logger.info("In unlock: lock forcibly released for " + lockId); + } + try { + getLockingServiceHandle().setLockState(lockName, mls); + } catch (MusicLockingException e) { + logger.error("Failed to release Lock " + lockName + " " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to release lock:" + (end - start) + " ms"); + return mls; + } + + /** + * + * @param lockName + */ + public static void deleteLock(String lockName) { + long start = System.currentTimeMillis(); + logger.info("Deleting lock for " + lockName); + try { + getLockingServiceHandle().deleteLock("/" + lockName); + } catch (MusicLockingException e) { + logger.error("Failed to Delete Lock " + lockName + " " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken to delete lock:" + (end - start) + " ms"); + } + + + + /** + * + * @param keyspace + * @param tablename + * @return + */ + public static TableMetadata returnColumnMetadata(String keyspace, String tablename) { + return getDSHandle().returnColumnMetadata(keyspace, tablename); + } + + + /** + * + * @param nodeName + */ + public static void pureZkCreate(String nodeName) { + try { + getLockingServiceHandle().getzkLockHandle().createNode(nodeName); + } catch (MusicLockingException e) { + logger.error("Failed to get ZK Lock Handle " + e); + } + } + + /** + * + * @param nodeName + * @param data + */ + public static void pureZkWrite(String nodeName, byte[] data) { + long start = System.currentTimeMillis(); + logger.info("Performing zookeeper write to " + nodeName); + try { + getLockingServiceHandle().getzkLockHandle().setNodeData(nodeName, data); + } catch (MusicLockingException e) { + logger.error("Failed to get ZK Lock Handle " + e); + } + logger.info("Performed zookeeper write to " + nodeName); + long end = System.currentTimeMillis(); + logger.info("Time taken for the actual zk put:" + (end - start) + " ms"); + } + + /** + * + * @param nodeName + * @return + */ + public static byte[] pureZkRead(String nodeName) { + long start = System.currentTimeMillis(); + byte[] data = null; + try { + data = getLockingServiceHandle().getzkLockHandle().getNodeData(nodeName); + } catch (MusicLockingException e) { + logger.error("Failed to get ZK Lock Handle " + e); + } + long end = System.currentTimeMillis(); + logger.info("Time taken for the actual zk put:" + (end - start) + " ms"); + return data; + } + + + + // Prepared Query Additions. + + /** + * + * @param keyspaceName + * @param tableName + * @param primaryKey + * @param queryObject + * @return ReturnType + * @throws MusicServiceException + */ + public static ReturnType eventualPut(PreparedQueryObject queryObject) { + boolean result = false; + try { + result = getDSHandle().executePut(queryObject, MusicUtil.EVENTUAL); + } catch (MusicServiceException | MusicQueryException ex) { + logger.error(ex.getMessage() + " " + ex.getCause() + " " + ex); + } + if (result) { + return new ReturnType(ResultType.SUCCESS, "Success"); + } else { + return new ReturnType(ResultType.FAILURE, "Failure"); + } + } + + /** + * + * @param keyspaceName + * @param tableName + * @param primaryKey + * @param queryObject + * @param lockId + * @return + */ + public static ReturnType criticalPut(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject, String lockId, Condition conditionInfo) { + long start = System.currentTimeMillis(); + + try { + MusicLockState mls = getLockingServiceHandle() + .getLockState(keyspaceName + "." + tableName + "." + primaryKey); + if (mls.getLockHolder().equals(lockId) == true) { + if (conditionInfo != null)// check if condition is true + if (conditionInfo.testCondition() == false) + return new ReturnType(ResultType.FAILURE, + "Lock acquired but the condition is not true"); + getDSHandle().executePut(queryObject, MusicUtil.CRITICAL); + long end = System.currentTimeMillis(); + logger.info("Time taken for the critical put:" + (end - start) + " ms"); + return new ReturnType(ResultType.SUCCESS, "Update performed"); + } else + return new ReturnType(ResultType.FAILURE, + "Cannot perform operation since you are the not the lock holder"); + } catch (MusicQueryException | MusicServiceException | MusicLockingException e) { + logger.error(e.getMessage()); + return new ReturnType(ResultType.FAILURE, + "Exception thrown while doing the critical put, check sanctity of the row/conditions:\n" + + e.getMessage()); + } + + } + + /** + * + * @param queryObject + * @param consistency + * @return Boolean Indicates success or failure + * + * + */ + public static boolean nonKeyRelatedPut(PreparedQueryObject queryObject, String consistency) { + // this is mainly for some functions like keyspace creation etc which does not + // really need the bells and whistles of Music locking. + boolean result = false; + try { + result = getDSHandle().executePut(queryObject, consistency); + } catch (MusicQueryException | MusicServiceException ex) { + logger.error(ex.getMessage()); + } + return result; + } + + /** + * This method performs DDL operation on cassandra. + * + * @param queryObject query object containing prepared query and values + * @return ResultSet + */ + public static ResultSet get(PreparedQueryObject queryObject) { + ResultSet results = null; + try { + results = getDSHandle().executeEventualGet(queryObject); + } catch (MusicQueryException | MusicServiceException e) { + logger.error(e.getMessage()); + } + return results; + } + + /** + * This method performs DDL operations on cassandra, if the the resource is available. Lock ID + * is used to check if the resource is free. + * + * @param keyspaceName name of the keyspace + * @param tableName name of the table + * @param primaryKey primary key value + * @param queryObject query object containing prepared query and values + * @param lockId lock ID to check if the resource is free to perform the operation. + * @return ResultSet + */ + public static ResultSet criticalGet(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject, String lockId) throws MusicServiceException { + ResultSet results = null; + try { + MusicLockState mls = getLockingServiceHandle() + .getLockState(keyspaceName + "." + tableName + "." + primaryKey); + if (mls.getLockHolder().equals(lockId)) { + results = getDSHandle().executeCriticalGet(queryObject); + } else + throw new MusicServiceException("YOU DO NOT HAVE THE LOCK"); + } catch (MusicQueryException | MusicServiceException | MusicLockingException e) { + logger.error(e.getMessage()); + } + return results; + } + + /** + * This method performs DML operation on cassandra, when the lock of the dd is acquired. + * + * @param keyspaceName name of the keyspace + * @param tableName name of the table + * @param primaryKey primary key value + * @param queryObject query object containing prepared query and values + * @return ReturnType + */ + public static ReturnType atomicPut(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject, Condition conditionInfo) { + long start = System.currentTimeMillis(); + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long lockCreationTime = System.currentTimeMillis(); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = acquireLockWithLease(key, lockId, leasePeriod); + long lockAcqTime = System.currentTimeMillis(); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + ReturnType criticalPutResult = criticalPut(keyspaceName, tableName, primaryKey, + queryObject, lockId, conditionInfo); + long criticalPutTime = System.currentTimeMillis(); + boolean voluntaryRelease = true; + deleteLock(key); + long lockDeleteTime = System.currentTimeMillis(); + String timingInfo = "|lock creation time:" + (lockCreationTime - start) + + "|lock accquire time:" + (lockAcqTime - lockCreationTime) + + "|critical put time:" + (criticalPutTime - lockAcqTime) + + "|lock delete time:" + (lockDeleteTime - criticalPutTime) + "|"; + criticalPutResult.setTimingInfo(timingInfo); + return criticalPutResult; + } else { + logger.info("unable to acquire lock, id " + lockId); + destroyLockRef(lockId); + return lockAcqResult; + } + } + + + /** + * This method performs DDL operation on cassasndra, when the lock for the resource is acquired. + * + * @param keyspaceName name of the keyspace + * @param tableName name of the table + * @param primaryKey primary key value + * @param queryObject query object containing prepared query and values + * @return ResultSet + * @throws MusicServiceException + */ + public static ResultSet atomicGet(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject) throws MusicServiceException { + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = acquireLockWithLease(key, lockId, leasePeriod); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + ResultSet result = + criticalGet(keyspaceName, tableName, primaryKey, queryObject, lockId); + boolean voluntaryRelease = true; + releaseLock(lockId, voluntaryRelease); + return result; + } else { + logger.info("unable to acquire lock, id " + lockId); + return null; + } + } + + /** + * authenticate user logic + * + * @param nameSpace + * @param userId + * @param password + * @param keyspace + * @param aid + * @param operation + * @return + * @throws Exception + */ + public static Map autheticateUser(String nameSpace, String userId, + String password, String keyspace, String aid, String operation) + throws Exception { + Map resultMap = new HashMap<>(); + String uuid = null; + resultMap = CachingUtil.validateRequest(nameSpace, userId, password, keyspace, aid, + operation); + if (!resultMap.isEmpty()) + return resultMap; + if (aid == null && (userId == null || password == null)) { + logger.error("One or more required headers is missing. userId: " + userId + + " :: password: " + password); + resultMap.put("Exception", + "UserId and Password are mandatory for the operation " + operation); + return resultMap; + } + boolean isAAF = CachingUtil.isAAFApplication(nameSpace); + if (!isAAF && aid != null && aid.length() > 0) { // Non AAF app + resultMap = CachingUtil.authenticateAIDUser(aid, keyspace); + if (!resultMap.isEmpty()) + return resultMap; + } + if (isAAF && nameSpace != null && userId != null && password != null) { + boolean isValid = true; + try { + isValid = CachingUtil.authenticateAAFUser(nameSpace, userId, password, keyspace); + } catch (Exception e) { + logger.error("Got exception while AAF authentication for namespace " + nameSpace); + resultMap.put("Exception", e.getMessage()); + // return resultMap; + } + if (!isValid) { + logger.error("User not authenticated with AAF."); + resultMap.put("Exception", "User not authenticated..."); + // return resultMap; + } + if (!resultMap.isEmpty()) + return resultMap; + + } + + if (operation.equals("createKeySpace")) { + logger.info("AID is not provided. Creating new UUID for keyspace."); + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "select uuid from admin.keyspace_master where application_name=? and username=? and keyspace_name=? allow filtering"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), nameSpace)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + + try { + Row rs = MusicCore.get(pQuery).one(); + uuid = rs.getUUID("uuid").toString(); + resultMap.put("uuid", "existing"); + } catch (Exception e) { + logger.info("No UUID found in DB. So creating new UUID."); + uuid = CachingUtil.generateUUID(); + resultMap.put("uuid", "new"); + } + + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "INSERT into admin.keyspace_master (uuid, keyspace_name, application_name, is_api, " + + "password, username, is_aaf) values (?,?,?,?,?,?,?)"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), keyspace)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), nameSpace)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), "True")); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), password)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF)); + CachingUtil.updateMusicCache(uuid, keyspace); + MusicCore.eventualPut(pQuery); + resultMap.put("aid", uuid); + } + + return resultMap; + } +} diff --git a/src/main/java/org/onap/music/main/MusicDigest.java b/src/main/java/org/onap/music/main/MusicDigest.java new file mode 100644 index 00000000..893cb51f --- /dev/null +++ b/src/main/java/org/onap/music/main/MusicDigest.java @@ -0,0 +1,78 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + +/** + * + * + */ +public class MusicDigest { + private String evPutStatus; + private String vectorTs; + + /** + * @param evPutStatus + * @param vectorTs + */ + public MusicDigest(String evPutStatus, String vectorTs) { + this.evPutStatus = evPutStatus; + this.vectorTs = vectorTs; + } + + /** + * @return + */ + public String getEvPutStatus() { + return evPutStatus; + } + + /** + * @param evPutStatus + */ + public void setEvPutStatus(String evPutStatus) { + this.evPutStatus = evPutStatus; + } + + /** + * @return + */ + public String getVectorTs() { + return vectorTs; + } + + /** + * @param vectorTs + */ + public void setVectorTs(String vectorTs) { + this.vectorTs = vectorTs; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() { + return vectorTs + "|" + evPutStatus; + } +} + diff --git a/src/main/java/org/onap/music/main/MusicUtil.java b/src/main/java/org/onap/music/main/MusicUtil.java new file mode 100755 index 00000000..61d428d1 --- /dev/null +++ b/src/main/java/org/onap/music/main/MusicUtil.java @@ -0,0 +1,465 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + + +import java.io.File; +import java.io.FileNotFoundException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.UUID; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.eelf.logging.EELFLoggerDelegate; +import com.datastax.driver.core.DataType; + +/** + * @author nelson24 + * + * Properties This will take Properties and load them into MusicUtil. This is a hack for + * now. Eventually it would bebest to do this in another way. + * + */ +public class MusicUtil { + private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(MusicUtil.class); + + private static int myId = 0; + private static ArrayList allIds = new ArrayList(); + private static String publicIp = ""; + private static ArrayList allPublicIps = new ArrayList(); + private static String myZkHost = "localhost"; + private static String myCassaHost = "localhost"; + private static String defaultMusicIp = "localhost"; + private static boolean debug = true; + private static String version = "2.3.0"; + public static String musicRestIp = "localhost"; + private static String musicPropertiesFilePath = "/opt/app/music/etc/music.properties"; + private static long defaultLockLeasePeriod = 6000; + private static final String[] propKeys = new String[] {"zookeeper.host", "cassandra.host", + "music.ip", "debug", "version", "music.rest.ip", "music.properties", + "lock.lease.period", "id", "all.ids", "public.ip", "all.pubic.ips", + "cassandra.user", "cassandra.password", "aaf.endpoint.url"}; + + public static final String ATOMIC = "atomic"; + public static final String EVENTUAL = "eventual"; + public static final String CRITICAL = "critical"; + public static final String DEFAULTKEYSPACENAME = "TBD"; + private static String cassName = "cassandra"; + private static String cassPwd = "cassandra"; + private static String aafEndpointUrl = null; + + /** + * @return the cassName + */ + public static String getCassName() { + return cassName; + } + + /** + * @return the cassPwd + */ + public static String getCassPwd() { + return cassPwd; + } + + /** + * @return the aafEndpointUrl + */ + public static String getAafEndpointUrl() { + return aafEndpointUrl; + } + + /** + * + * @param aafEndpointUrl + */ + public static void setAafEndpointUrl(String aafEndpointUrl) { + MusicUtil.aafEndpointUrl = aafEndpointUrl; + } + + /** + * + * @return + */ + public static int getMyId() { + return myId; + } + + /** + * + * @param myId + */ + public static void setMyId(int myId) { + MusicUtil.myId = myId; + } + + + /** + * + * @return + */ + public static ArrayList getAllIds() { + return allIds; + } + + /** + * + * @param allIds + */ + public static void setAllIds(List allIds) { + MusicUtil.allIds = (ArrayList) allIds; + } + + /** + * + * @return + */ + public static String getPublicIp() { + return publicIp; + } + + /** + * + * @param publicIp + */ + public static void setPublicIp(String publicIp) { + MusicUtil.publicIp = publicIp; + } + + /** + * + * @return + */ + public static ArrayList getAllPublicIps() { + return allPublicIps; + } + + /** + * + * @param allPublicIps + */ + public static void setAllPublicIps(List allPublicIps) { + MusicUtil.allPublicIps = (ArrayList) allPublicIps; + } + + /** + * Returns An array of property names that should be in the Properties files. + * + * @return + */ + public static String[] getPropkeys() { + return propKeys; + } + + /** + * Get MusicRestIp - default = localhost property file value - music.rest.ip + * + * @return + */ + public static String getMusicRestIp() { + return musicRestIp; + } + + /** + * Set MusicRestIp + * + * @param musicRestIp + */ + public static void setMusicRestIp(String musicRestIp) { + MusicUtil.musicRestIp = musicRestIp; + } + + /** + * Get MusicPropertiesFilePath - Default = /opt/music/music.properties property file value - + * music.properties + * + * @return + */ + public static String getMusicPropertiesFilePath() { + return musicPropertiesFilePath; + } + + /** + * Set MusicPropertiesFilePath + * + * @param musicPropertiesFilePath + */ + public static void setMusicPropertiesFilePath(String musicPropertiesFilePath) { + MusicUtil.musicPropertiesFilePath = musicPropertiesFilePath; + } + + /** + * Get DefaultLockLeasePeriod - Default = 6000 property file value - lock.lease.period + * + * @return + */ + public static long getDefaultLockLeasePeriod() { + return defaultLockLeasePeriod; + } + + /** + * Set DefaultLockLeasePeriod + * + * @param defaultLockLeasePeriod + */ + public static void setDefaultLockLeasePeriod(long defaultLockLeasePeriod) { + MusicUtil.defaultLockLeasePeriod = defaultLockLeasePeriod; + } + + /** + * Set Debug + * + * @param debug + */ + public static void setDebug(boolean debug) { + MusicUtil.debug = debug; + } + + /** + * Is Debug - Default = true property file value - debug + * + * @return + */ + public static boolean isDebug() { + return debug; + } + + /** + * Set Version + * + * @param version + */ + public static void setVersion(String version) { + MusicUtil.version = version; + } + + /** + * Return the version property file value - version + * + * @return + */ + public static String getVersion() { + return version; + } + + /** + * Get MyZkHost - Zookeeper Hostname - Default = localhost property file value - zookeeper.host + * + * @return + */ + public static String getMyZkHost() { + return myZkHost; + } + + /** + * Set MyZkHost - Zookeeper Hostname + * + * @param myZkHost + */ + public static void setMyZkHost(String myZkHost) { + MusicUtil.myZkHost = myZkHost; + } + + /** + * Get MyCassHost - Cassandra Hostname - Default = localhost property file value - + * cassandra.host + * + * @return + */ + public static String getMyCassaHost() { + return myCassaHost; + } + + /** + * Set MyCassHost - Cassandra Hostname + * + * @param myCassaHost + */ + public static void setMyCassaHost(String myCassaHost) { + MusicUtil.myCassaHost = myCassaHost; + } + + /** + * Get DefaultMusicIp - Default = localhost property file value - music.ip + * + * @return + */ + public static String getDefaultMusicIp() { + return defaultMusicIp; + } + + /** + * Set DefaultMusicIp + * + * @param defaultMusicIp + */ + public static void setDefaultMusicIp(String defaultMusicIp) { + MusicUtil.defaultMusicIp = defaultMusicIp; + } + + /** + * + * @return + */ + public static String getTestType() { + String testType = ""; + try { + Scanner fileScanner = new Scanner(new File("")); + testType = fileScanner.next();// ignore the my id line + String batchSize = fileScanner.next();// ignore the my public ip line + fileScanner.close(); + } catch (FileNotFoundException e) { + logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); + } + return testType; + + } + + /** + * + * @param time + */ + public static void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); + } + } + + /** + * Utility function to check if the query object is valid. + * + * @param withparams + * @param queryObject + * @return + */ + public static boolean isValidQueryObject(boolean withparams, PreparedQueryObject queryObject) { + if (withparams) { + int noOfValues = queryObject.getValues().size(); + int noOfParams = 0; + char[] temp = queryObject.getQuery().toCharArray(); + for (int i = 0; i < temp.length; i++) { + if (temp[i] == '?') + noOfParams++; + } + return (noOfValues == noOfParams); + } else { + return !queryObject.getQuery().isEmpty(); + } + + } + + public static void setCassName(String cassName) { + MusicUtil.cassName = cassName; + } + + public static void setCassPwd(String cassPwd) { + MusicUtil.cassPwd = cassPwd; + } + + public static String convertToCQLDataType(DataType type, Object valueObj) { + + String value = ""; + switch (type.getName()) { + case UUID: + value = valueObj + ""; + break; + case TEXT: + case VARCHAR: + String valueString = valueObj + ""; + valueString = valueString.replace("'", "''"); + value = "'" + valueString + "'"; + break; + case MAP: { + Map otMap = (Map) valueObj; + value = "{" + jsonMaptoSqlString(otMap, ",") + "}"; + break; + } + default: + value = valueObj + ""; + break; + } + return value; + } + + + /** + * + * @param colType + * @param valueObj + * @return + * @throws Exception + */ + public static Object convertToActualDataType(DataType colType, Object valueObj) { + String valueObjString = valueObj + ""; + switch (colType.getName()) { + case UUID: + return UUID.fromString(valueObjString); + case VARINT: + return BigInteger.valueOf(Long.parseLong(valueObjString)); + case BIGINT: + return Long.parseLong(valueObjString); + case INT: + return Integer.parseInt(valueObjString); + case FLOAT: + return Float.parseFloat(valueObjString); + case DOUBLE: + return Double.parseDouble(valueObjString); + case BOOLEAN: + return Boolean.parseBoolean(valueObjString); + case MAP: + return (Map) valueObj; + default: + return valueObjString; + } + } + + + /** + * + * Utility function to parse json map into sql like string + * + * @param jMap + * @param lineDelimiter + * @return + */ + + public static String jsonMaptoSqlString(Map jMap, String lineDelimiter) { + StringBuilder sqlString = new StringBuilder(); + int counter = 0; + for (Map.Entry entry : jMap.entrySet()) { + Object ot = entry.getValue(); + String value = ot + ""; + if (ot instanceof String) { + value = "'" + value.replace("'", "''") + "'"; + } + sqlString.append("'" + entry.getKey() + "':" + value); + if (counter != jMap.size() - 1) + sqlString.append(lineDelimiter); + counter = counter + 1; + } + return sqlString.toString(); + } +} diff --git a/src/main/java/org/onap/music/main/PropertiesListener.java b/src/main/java/org/onap/music/main/PropertiesListener.java new file mode 100755 index 00000000..de231542 --- /dev/null +++ b/src/main/java/org/onap/music/main/PropertiesListener.java @@ -0,0 +1,147 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Properties; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; +import org.onap.music.eelf.logging.EELFLoggerDelegate; + +@WebListener +public class PropertiesListener implements ServletContextListener { + private Properties prop; + + private static EELFLoggerDelegate logger = + EELFLoggerDelegate.getLogger(PropertiesListener.class); + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) { + prop = new Properties(); + Properties projectProp = new Properties(); + URL resource = getClass().getResource("/"); + String musicPropertiesFilePath = resource.getPath().replace("WEB-INF/classes/", + "WEB-INF/classes/project.properties"); + + // Open the file + try { + InputStream musicProps = null; + projectProp.load(new FileInputStream(musicPropertiesFilePath)); + if (projectProp.containsKey("music.properties")) { + musicProps = new FileInputStream(projectProp.getProperty("music.properties")); + } else { + musicProps = new FileInputStream(MusicUtil.getMusicPropertiesFilePath()); + } + prop.load(musicProps); + musicProps.close(); + prop.putAll(projectProp); + String[] propKeys = MusicUtil.getPropkeys(); + for (int k = 0; k < propKeys.length; k++) { + String key = propKeys[k]; + if (prop.containsKey(key) && prop.get(key) != null) { + logger.info(key + " : " + prop.getProperty(key)); + switch (key) { + case "zookeeper.host": + MusicUtil.setMyZkHost(prop.getProperty(key)); + break; + case "cassandra.host": + MusicUtil.setMyCassaHost(prop.getProperty(key)); + break; + case "music.ip": + MusicUtil.setDefaultMusicIp(prop.getProperty(key)); + break; + case "debug": + MusicUtil.setDebug(Boolean + .getBoolean(prop.getProperty(key).toLowerCase())); + break; + case "version": + MusicUtil.setVersion(prop.getProperty(key)); + break; + case "music.rest.ip": + MusicUtil.setMusicRestIp(prop.getProperty(key)); + break; + case "music.properties": + MusicUtil.setMusicPropertiesFilePath(prop.getProperty(key)); + break; + case "lock.lease.period": + MusicUtil.setDefaultLockLeasePeriod( + Long.parseLong(prop.getProperty(key))); + break; + case "my.id": + MusicUtil.setMyId(Integer.parseInt(prop.getProperty(key))); + break; + case "all.ids": + String[] ids = prop.getProperty(key).split(":"); + MusicUtil.setAllIds(new ArrayList(Arrays.asList(ids))); + break; + case "public.ip": + MusicUtil.setPublicIp(prop.getProperty(key)); + break; + case "all.public.ips": + String[] ips = prop.getProperty(key).split(":"); + if (ips.length == 1) { + // Future use + } else if (ips.length > 1) { + MusicUtil.setAllPublicIps( + new ArrayList(Arrays.asList(ips))); + } + break; + case "cassandra.user": + MusicUtil.setCassName(prop.getProperty(key)); + break; + case "cassandra.password": + MusicUtil.setCassPwd(prop.getProperty(key)); + break; + case "aaf.endpoint.url": + MusicUtil.setAafEndpointUrl(prop.getProperty(key)); + break; + default: + logger.error(EELFLoggerDelegate.errorLogger, + "No case found for " + key); + } + } + } + } catch (IOException e) { + logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); + } + + logger.info(EELFLoggerDelegate.applicationLogger, + "Starting MUSIC " + MusicUtil.getVersion() + " on node with id " + + MusicUtil.getMyId() + " and public ip " + + MusicUtil.getPublicIp() + "..."); + logger.info(EELFLoggerDelegate.applicationLogger, + "List of all MUSIC ids:" + MusicUtil.getAllIds().toString()); + logger.info(EELFLoggerDelegate.applicationLogger, + "List of all MUSIC public ips:" + MusicUtil.getAllPublicIps().toString()); + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) { + prop = null; + } +} diff --git a/src/main/java/org/onap/music/main/ResultType.java b/src/main/java/org/onap/music/main/ResultType.java new file mode 100644 index 00000000..f19ada4a --- /dev/null +++ b/src/main/java/org/onap/music/main/ResultType.java @@ -0,0 +1,39 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + +public enum ResultType { + SUCCESS("Success"), FAILURE("Failure"); + + private String result; + + ResultType(String result) { + this.result = result; + } + + public String getResult() { + return result; + } + +} + + diff --git a/src/main/java/org/onap/music/main/ReturnType.java b/src/main/java/org/onap/music/main/ReturnType.java new file mode 100644 index 00000000..1453a1bf --- /dev/null +++ b/src/main/java/org/onap/music/main/ReturnType.java @@ -0,0 +1,74 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.main; + +import java.util.HashMap; +import java.util.Map; + +public class ReturnType { + private ResultType result; + private String message; + + public ReturnType(ResultType result, String message) { + super(); + this.result = result; + this.message = message; + } + + public String getTimingInfo() { + return timingInfo; + } + + public void setTimingInfo(String timingInfo) { + this.timingInfo = timingInfo; + } + + private String timingInfo; + + public ResultType getResult() { + return result; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String toJson() { + return "{ \"result\":\"" + result.getResult() + "\", \"message\":\"" + message + "\"}"; + } + + public String toString() { + return result + " | " + message; + } + + public Map toMap() { + Map newMap = new HashMap<>(); + newMap.put("result", result.getResult()); + newMap.put("message", message); + return newMap; + } + +} diff --git a/src/main/java/org/onap/music/response/jsonobjects/JsonLockResponse.java b/src/main/java/org/onap/music/response/jsonobjects/JsonLockResponse.java new file mode 100644 index 00000000..875fbbba --- /dev/null +++ b/src/main/java/org/onap/music/response/jsonobjects/JsonLockResponse.java @@ -0,0 +1,258 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.response.jsonobjects; + +import java.util.HashMap; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonResponse", description = "General Response JSON") +public class JsonLockResponse { + + private String status = ""; + private String error = ""; + private String message = ""; + private String lock = ""; + private String lockStatus = ""; + private String lockHolder = ""; + private String lockLease = ""; + + /** + * + * @param status + * @param error + * @param lock + */ + public JsonLockResponse(String status, String error, String lock) { + this.status = fixStatus(status); + this.error = error; + this.lock = lock; + } + + /** + * + * @param status + * @param error + * @param lock + * @param lockStatus + * @param lockHolder + */ + public JsonLockResponse(String status, String error, String lock, String lockStatus, + String lockHolder) { + this.status = fixStatus(status); + this.error = error; + this.lock = lock; + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + } + + /** + * + * @param status + * @param error + * @param lock + * @param lockStatus + * @param lockHolder + * @param lockLease + */ + public JsonLockResponse(String status, String error, String lock, String lockStatus, + String lockHolder, String lockLease) { + this.status = fixStatus(status); + this.error = error; + this.lock = lock; + this.lockStatus = lockStatus; + this.lockHolder = lockHolder; + } + + + /** + * Lock + * + * @return + */ + public String getLock() { + return lock; + } + + /** + * + * @param lock + */ + public void setLock(String lock) { + this.lock = lock; + } + + /** + * + */ + public JsonLockResponse() { + this.status = ""; + this.error = ""; + } + + /** + * + * @param statusIn + * @return + */ + private String fixStatus(String statusIn) { + if (statusIn.equalsIgnoreCase("false")) { + return "FAILURE"; + } + return "SUCCESS"; + } + + /** + * + * @return + */ + @ApiModelProperty(value = "Overall status of the response.", + allowableValues = "Success,Failure") + public String getStatus() { + return status; + } + + /** + * + * @param status + */ + public void setStatus(String status) { + this.status = fixStatus(status); + } + + /** + * + * @return the error + */ + @ApiModelProperty(value = "Error value") + public String getError() { + return error; + } + + /** + * + * @param error + */ + public void setError(String error) { + this.error = error; + } + + /** + * + * @return the message + */ + @ApiModelProperty(value = "Message if any need to be conveyed about the lock") + public String getMessage() { + return message; + } + + /** + * + * @param message + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * + * @return the lockStatus + */ + @ApiModelProperty(value = "Status of the lock", + allowableValues = "UNLOCKED,BEING_LOCKED,LOCKED") + public String getLockStatus() { + return lockStatus; + } + + /** + * + * @param lockStatus + */ + public void setLockStatus(String lockStatus) { + this.lockStatus = lockStatus; + } + + /** + * + * + * @return the lockHolder + */ + @ApiModelProperty(value = "Holder of the Lock") + public String getLockHolder() { + return lockHolder; + } + + /** + * + * @param lockHolder + */ + public void setLockHolder(String lockHolder) { + this.lockHolder = lockHolder; + } + + + + /** + * @return the lockLease + */ + public String getLockLease() { + return lockLease; + } + + /** + * @param lockLease the lockLease to set + */ + public void setLockLease(String lockLease) { + this.lockLease = lockLease; + } + + /** + * Convert to Map + * + * @return + */ + public Map toMap() { + Map newMap = new HashMap<>(); + Map lockMap = new HashMap<>(); + lockMap.put("lock-status", lockStatus); + lockMap.put("lock", lock); + lockMap.put("message", message); + lockMap.put("lock-holder", lockHolder); + lockMap.put("lock-lease", lockLease); + newMap.put("status", status); + newMap.put("error", error); + newMap.put("lock", lockMap); + return newMap; + } + + /** + * Convert to String + */ + @Override + public String toString() { + return "JsonLockResponse [status=" + status + ", error=" + error + ", message=" + message + + ", lock=" + lock + ", lockStatus=" + lockStatus + ", lockHolder=" + + lockHolder + "]"; + } + +} diff --git a/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java b/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java new file mode 100644 index 00000000..d44f9fe7 --- /dev/null +++ b/src/main/java/org/onap/music/response/jsonobjects/JsonResponse.java @@ -0,0 +1,95 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.response.jsonobjects; + + +import java.util.HashMap; +import java.util.Map; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel(value = "JsonResponse", description = "General Response JSON") +public class JsonResponse { + + private Boolean status = false; + private String error = ""; + private String version = ""; + + public JsonResponse(Boolean status, String error, String version) { + this.status = status; + this.error = error; + this.version = version; + } + + public JsonResponse() { + this.status = false; + this.error = ""; + this.version = ""; + } + + @ApiModelProperty(value = "Status value") + public Boolean getStatus() { + return status; + } + + /** + * + * @param statusIn + * @return + */ + private String fixStatus(String statusIn) { + if (statusIn.equalsIgnoreCase("false")) { + return "FAILURE"; + } + return "SUCCESS"; + } + + public void setStatus(Boolean status) { + this.status = status; + } + + @ApiModelProperty(value = "Error value") + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } + + @ApiModelProperty(value = "Version value") + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Map toMap() { + Map newMap = new HashMap<>(); + newMap.put("status", fixStatus(String.valueOf(status))); + newMap.put("error", error); + newMap.put("version", version); + return newMap; + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicAdminAPI.java b/src/main/java/org/onap/music/rest/RestMusicAdminAPI.java new file mode 100755 index 00000000..87a3a1ba --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicAdminAPI.java @@ -0,0 +1,305 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.datastore.jsonobjects.JsonOnboard; +import org.onap.music.main.CachingUtil; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +@Path("/v{version: [0-9]+}/admin") +// @Path("/admin") +@Api(value = "Admin Api", hidden = true) +public class RestMusicAdminAPI { + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicAdminAPI.class); + + /* + * API to onboard an application with MUSIC. This is the mandatory first step. + * + */ + @POST + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map onboardAppWithMusic(JsonOnboard jsonObj, + @Context HttpServletResponse response) throws Exception { + Map resultMap = new HashMap<>(); + String appName = jsonObj.getAppname(); + String userId = jsonObj.getUserId(); + String isAAF = jsonObj.getIsAAF(); + String password = jsonObj.getPassword(); + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + if (appName == null || userId == null || isAAF == null || password == null) { + resultMap.put("Exception", + "Please check the request parameters. Some of the required values appName(ns), userId, password, isAAF are missing."); + return resultMap; + } + + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "select uuid from admin.keyspace_master where application_name = ? allow filtering"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + ResultSet rs = MusicCore.get(pQuery); + if (!rs.all().isEmpty()) { + resultMap.put("Exception", "Your application " + appName + + " has already been onboarded. Please contact admin."); + return resultMap; + } + + pQuery = new PreparedQueryObject(); + String uuid = CachingUtil.generateUUID(); + pQuery.appendQueryString( + "INSERT INTO admin.keyspace_master (uuid, keyspace_name, application_name, is_api, " + + "password, username, is_aaf) VALUES (?,?,?,?,?,?,?)"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), "True")); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), password)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF)); + + String returnStr = MusicCore.eventualPut(pQuery).toString(); + if (returnStr.contains("Failure")) { + resultMap.put("Exception", + "Oops. Something wrong with onboarding process. Please retry later or contact admin."); + return resultMap; + } + CachingUtil.updateisAAFCache(appName, isAAF); + resultMap.put("Success", "Your application " + appName + " has been onboarded with MUSIC."); + resultMap.put("Generated AID", uuid); + return resultMap; + } + + + /* + * API to onboard an application with MUSIC. This is the mandatory first step. + * + */ + @GET + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map getOnboardedInfo( + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String uuid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String appName, + @Context HttpServletResponse response) throws Exception { + Map resultMap = new HashMap<>(); + + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + if (appName == null && uuid == null) { + resultMap.put("Exception", + "Please check the request parameters. Some of the required values appName(ns), aid are missing."); + return resultMap; + } + + PreparedQueryObject pQuery = new PreparedQueryObject(); + + String cql = "select uuid, keyspace_name from admin.keyspace_master where "; + if (appName != null) + cql = cql + "application_name = ?"; + else if (uuid != null) + cql = cql + "uuid = ?"; + cql = cql + " allow filtering"; + System.out.println("Get OnboardingInfo CQL: " + cql); + pQuery.appendQueryString(cql); + if (appName != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + else if (uuid != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + ResultSet rs = MusicCore.get(pQuery); + Iterator it = rs.iterator(); + while (it.hasNext()) { + Row row = (Row) it.next(); + resultMap.put(row.getString("keyspace_name"), row.getUUID("uuid")); + } + if (resultMap.isEmpty()) + resultMap.put("ERROR", "Application is not onboarded. Please contact admin."); + return resultMap; + } + + + @DELETE + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Delete Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map deleteOnboardApp(JsonOnboard jsonObj, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @Context HttpServletResponse response) throws Exception { + Map resultMap = new HashMap<>(); + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + String appName = jsonObj.getAppname(); + PreparedQueryObject pQuery = new PreparedQueryObject(); + long count = 0; + if (appName == null && aid == null) { + resultMap.put("Exception", "Please make sure either appName(ns) or Aid is present"); + return resultMap; + } + if (aid != null) { + pQuery.appendQueryString( + "select count(*) as count from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), + UUID.fromString(aid))); + Row row = MusicCore.get(pQuery).one(); + if (row != null) { + count = row.getLong(0); + } + + if (count == 0) { + resultMap.put("Failure", "Please verify your AID."); + return resultMap; + } else { + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), + UUID.fromString(aid))); + String result = MusicCore.eventualPut(pQuery).toString(); + if (result.toLowerCase().contains("success")) { + resultMap.put("Success", "Your application has been deleted."); + return resultMap; + } else { + resultMap.put("Failure", "Please verify your AID."); + return resultMap; + } + } + + } + pQuery.appendQueryString( + "select uuid from admin.keyspace_master where application_name = ? allow filtering"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + ResultSet rs = MusicCore.get(pQuery); + List rows = rs.all(); + String uuid = null; + if (rows.size() == 0) { + resultMap.put("Exception", + "Application not found. Please make sure Application exists."); + return resultMap; + } else if (rows.size() == 1) { + uuid = rows.get(0).getUUID("uuid").toString(); + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), + UUID.fromString(uuid))); + MusicCore.eventualPut(pQuery); + resultMap.put("Success", "Your application " + appName + " has been deleted."); + return resultMap; + } else { + resultMap.put("Failure", "Please provide UUID for the application."); + } + + return resultMap; + } + + + @PUT + @Path("/onboardAppWithMusic") + @ApiOperation(value = "Update Onboard application", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map updateOnboardApp(JsonOnboard jsonObj, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @Context HttpServletResponse response) throws Exception { + Map resultMap = new HashMap<>(); + response.addHeader("X-latestVersion", MusicUtil.getVersion()); + String appName = jsonObj.getAppname(); + String userId = jsonObj.getUserId(); + String isAAF = jsonObj.getIsAAF(); + String password = jsonObj.getPassword(); + String consistency = "eventual"; + PreparedQueryObject pQuery = new PreparedQueryObject(); + + if (aid == null) { + resultMap.put("Exception", "Please make sure Aid is present"); + return resultMap; + } + + if (appName == null && userId == null && password == null && isAAF == null) { + resultMap.put("Exception", + "No parameters found to update. Please update atleast one parameter."); + return resultMap; + } + + StringBuilder preCql = new StringBuilder("UPDATE admin.keyspace_master SET "); + if (appName != null) + preCql.append(" application_name = ?,"); + if (userId != null) + preCql.append(" username = ?,"); + if (password != null) + preCql.append(" password = ?,"); + if (isAAF != null) + preCql.append(" is_aaf = ?,"); + preCql.deleteCharAt(preCql.length() - 1); + preCql.append(" WHERE uuid = ?"); + pQuery.appendQueryString(preCql.toString()); + if (appName != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + if (userId != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + if (password != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), password)); + if (isAAF != null) + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), isAAF)); + + + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), UUID.fromString(aid))); + Boolean result = MusicCore.nonKeyRelatedPut(pQuery, consistency); + + if (result) { + resultMap.put("Success", "Your application has been updated successfully"); + } else { + resultMap.put("Exception", + "Oops. Spomething went wrong. Please make sure Aid is correct and application is onboarded"); + } + + return resultMap; + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicBmAPI.java b/src/main/java/org/onap/music/rest/RestMusicBmAPI.java new file mode 100644 index 00000000..90b82229 --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicBmAPI.java @@ -0,0 +1,313 @@ +/* + * ============LICENSE_START========================================== org.onap.music + * =================================================================== Copyright (c) 2017 AT&T + * Intellectual Property =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import org.apache.log4j.Logger; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonOnboard; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.main.CachingUtil; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.TableMetadata; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +/* + * These are functions created purely for benchmarking purposes. Commented out Swagger - This should + * be undocumented API + * + */ +@Path("/v{version: [0-9]+}/benchmarks/") +@Api(value = "Benchmark API", hidden = true) +public class RestMusicBmAPI { + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicBmAPI.class); + + // pure zk calls... + + /** + * + * @param nodeName + * @throws Exception + */ + @POST + @Path("/purezk/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkCreate(@PathParam("name") String nodeName) throws Exception { + MusicCore.pureZkCreate("/" + nodeName); + } + + + /** + * + * @param insObj + * @param nodeName + * @throws Exception + */ + @PUT + @Path("/purezk/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkUpdate(JsonInsert insObj, @PathParam("name") String nodeName) + throws Exception { + logger.info("--------------Zk normal update-------------------------"); + long start = System.currentTimeMillis(); + MusicCore.pureZkWrite(nodeName, insObj.serialize()); + long end = System.currentTimeMillis(); + logger.info("Total time taken for Zk normal update:" + (end - start) + " ms"); + } + + /** + * + * @param nodeName + * @return + * @throws Exception + */ + @GET + @Path("/purezk/{name}") + @Consumes(MediaType.TEXT_PLAIN) + public byte[] pureZkGet(@PathParam("name") String nodeName) throws Exception { + return MusicCore.pureZkRead(nodeName); + } + + /** + * + * @param insObj + * @param lockName + * @param nodeName + * @throws Exception + */ + @PUT + @Path("/purezk/atomic/{lockname}/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkAtomicPut(JsonInsert updateObj, @PathParam("lockname") String lockname, + @PathParam("name") String nodeName) throws Exception { + long startTime = System.currentTimeMillis(); + String operationId = UUID.randomUUID().toString();// just for debugging purposes. + String consistency = updateObj.getConsistencyInfo().get("type"); + + logger.info("--------------Zookeeper " + consistency + " update-" + operationId + + "-------------------------"); + + byte[] data = updateObj.serialize(); + long jsonParseCompletionTime = System.currentTimeMillis(); + + String lockId = MusicCore.createLockReference(lockname); + + long lockCreationTime = System.currentTimeMillis(); + + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = MusicCore.acquireLockWithLease(lockname, lockId, leasePeriod); + long lockAcqTime = System.currentTimeMillis(); + long zkPutTime = 0, lockReleaseTime = 0; + + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + MusicCore.pureZkWrite(lockname, data); + zkPutTime = System.currentTimeMillis(); + boolean voluntaryRelease = true; + if (consistency.equals("atomic")) + MusicCore.releaseLock(lockId, voluntaryRelease); + else if (consistency.equals("atomic_delete_lock")) + MusicCore.deleteLock(lockname); + lockReleaseTime = System.currentTimeMillis(); + } else { + MusicCore.destroyLockRef(lockId); + } + + long actualUpdateCompletionTime = System.currentTimeMillis(); + + + long endTime = System.currentTimeMillis(); + + String lockingInfo = "|lock creation time:" + (lockCreationTime - jsonParseCompletionTime) + + "|lock accquire time:" + (lockAcqTime - lockCreationTime) + + "|zk put time:" + (zkPutTime - lockAcqTime); + + if (consistency.equals("atomic")) + lockingInfo = lockingInfo + "|lock release time:" + (lockReleaseTime - zkPutTime) + "|"; + else if (consistency.equals("atomic_delete_lock")) + lockingInfo = lockingInfo + "|lock delete time:" + (lockReleaseTime - zkPutTime) + "|"; + + String timingString = "Time taken in ms for Zookeeper " + consistency + " update-" + + operationId + ":" + "|total operation time:" + (endTime - startTime) + + "|json parsing time:" + (jsonParseCompletionTime - startTime) + + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime) + + lockingInfo; + + logger.info(timingString); + } + + /** + * + * @param insObj + * @param lockName + * @param nodeName + * @throws Exception + */ + @GET + @Path("/purezk/atomic/{lockname}/{name}") + @Consumes(MediaType.APPLICATION_JSON) + public void pureZkAtomicGet(JsonInsert insObj, @PathParam("lockname") String lockName, + @PathParam("name") String nodeName) throws Exception { + logger.info("--------------Zk atomic read-------------------------"); + long start = System.currentTimeMillis(); + String lockId = MusicCore.createLockReference(lockName); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + ReturnType lockAcqResult = MusicCore.acquireLockWithLease(lockName, lockId, leasePeriod); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info("acquired lock with id " + lockId); + MusicCore.pureZkRead(nodeName); + boolean voluntaryRelease = true; + MusicCore.releaseLock(lockId, voluntaryRelease); + } else { + MusicCore.destroyLockRef(lockId); + } + + long end = System.currentTimeMillis(); + logger.info("Total time taken for Zk atomic read:" + (end - start) + " ms"); + } + + /** + * + * doing an update directly to cassa but through the rest api + * + * @param insObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @PUT + @Path("/cassa/keyspaces/{keyspace}/tables/{tablename}/rows") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public boolean updateTableCassa(JsonInsert insObj, @PathParam("keyspace") String keyspace, + @PathParam("tablename") String tablename, @Context UriInfo info) + throws Exception { + long startTime = System.currentTimeMillis(); + String operationId = UUID.randomUUID().toString();// just for debugging purposes. + String consistency = insObj.getConsistencyInfo().get("type"); + logger.info("--------------Cassandra " + consistency + " update-" + operationId + + "-------------------------"); + PreparedQueryObject queryObject = new PreparedQueryObject(); + Map valuesMap = insObj.getValues(); + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + String vectorTs = "'" + Thread.currentThread().getId() + System.currentTimeMillis() + "'"; + String fieldValueString = "vector_ts= ? ,"; + queryObject.addValue(vectorTs); + + int counter = 0; + for (Map.Entry entry : valuesMap.entrySet()) { + Object valueObj = entry.getValue(); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object valueString = MusicUtil.convertToActualDataType(colType, valueObj); + fieldValueString = fieldValueString + entry.getKey() + "= ?"; + queryObject.addValue(valueString); + if (counter != valuesMap.size() - 1) + fieldValueString = fieldValueString + ","; + counter = counter + 1; + } + + // get the row specifier + String rowSpec = ""; + counter = 0; + queryObject.appendQueryString("UPDATE " + keyspace + "." + tablename + " "); + MultivaluedMap rowParams = info.getQueryParameters(); + String primaryKey = ""; + for (MultivaluedMap.Entry> entry : rowParams.entrySet()) { + String keyName = entry.getKey(); + List valueList = entry.getValue(); + String indValue = valueList.get(0); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object formattedValue = MusicUtil.convertToActualDataType(colType, indValue); + primaryKey = primaryKey + indValue; + rowSpec = rowSpec + keyName + "= ? "; + queryObject.addValue(formattedValue); + if (counter != rowParams.size() - 1) + rowSpec = rowSpec + " AND "; + counter = counter + 1; + } + + + String ttl = insObj.getTtl(); + String timestamp = insObj.getTimestamp(); + + if ((ttl != null) && (timestamp != null)) { + + logger.info("both there"); + queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?"); + queryObject.addValue(Integer.parseInt(ttl)); + queryObject.addValue(Long.parseLong(timestamp)); + } + + if ((ttl != null) && (timestamp == null)) { + logger.info("ONLY TTL there"); + queryObject.appendQueryString(" USING TTL ?"); + queryObject.addValue(Integer.parseInt(ttl)); + } + + if ((ttl == null) && (timestamp != null)) { + logger.info("ONLY timestamp there"); + queryObject.appendQueryString(" USING TIMESTAMP ?"); + queryObject.addValue(Long.parseLong(timestamp)); + } + queryObject.appendQueryString(" SET " + fieldValueString + " WHERE " + rowSpec + ";"); + + long jsonParseCompletionTime = System.currentTimeMillis(); + + boolean operationResult = true; + MusicCore.getDSHandle().executePut(queryObject, insObj.getConsistencyInfo().get("type")); + + long actualUpdateCompletionTime = System.currentTimeMillis(); + + long endTime = System.currentTimeMillis(); + + String timingString = "Time taken in ms for Cassandra " + consistency + " update-" + + operationId + ":" + "|total operation time:" + (endTime - startTime) + + "|json parsing time:" + (jsonParseCompletionTime - startTime) + + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime) + + "|"; + logger.info(timingString); + + return operationResult; + } + +} diff --git a/src/main/java/org/onap/music/rest/RestMusicDataAPI.java b/src/main/java/org/onap/music/rest/RestMusicDataAPI.java new file mode 100755 index 00000000..ba0f1a3b --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicDataAPI.java @@ -0,0 +1,1088 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +// import java.util.logging.Level; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.UriInfo; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonKeySpace; +import org.onap.music.response.jsonobjects.JsonResponse; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.main.CachingUtil; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.main.ResultType; +import org.onap.music.main.ReturnType; +import org.onap.music.main.MusicCore.Condition; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.TableMetadata; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +@Path("/v{version: [0-9]+}/keyspaces") +@Api(value = "Data Api") +public class RestMusicDataAPI { + /* + * Header values for Versioning X-minorVersion *** - Used to request or communicate a MINOR + * version back from the client to the server, and from the server back to the client - This + * will be the MINOR version requested by the client, or the MINOR version of the last MAJOR + * version (if not specified by the client on the request) - Contains a single position value + * (e.g. if the full version is 1.24.5, X-minorVersion = "24") - Is optional for the client on + * request; however, this header should be provided if the client needs to take advantage of + * MINOR incremented version functionality - Is mandatory for the server on response + * + *** X-patchVersion *** - Used only to communicate a PATCH version in a response for + * troubleshooting purposes only, and will not be provided by the client on request - This will + * be the latest PATCH version of the MINOR requested by the client, or the latest PATCH version + * of the MAJOR (if not specified by the client on the request) - Contains a single position + * value (e.g. if the full version is 1.24.5, X-patchVersion = "5") - Is mandatory for the + * server on response + * + *** X-latestVersion *** - Used only to communicate an API's latest version - Is mandatory for the + * server on response, and shall include the entire version of the API (e.g. if the full version + * is 1.24.5, X-latestVersion = "1.24.5") - Used in the response to inform clients that they are + * not using the latest version of the API + * + */ + + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicDataAPI.class); + private static String xLatestVersion = "X-latestVersion"; + + private class RowIdentifier { + public String primarKeyValue; + public StringBuilder rowIdString; + public PreparedQueryObject queryObject;// the string with all the row + // identifiers separated by AND + + public RowIdentifier(String primaryKeyValue, StringBuilder rowIdString, + PreparedQueryObject queryObject) { + this.primarKeyValue = primaryKeyValue; + this.rowIdString = rowIdString; + this.queryObject = queryObject; + } + } + + private String buildVersion(String major, String minor, String patch) { + if (minor != null) { + major += "." + minor; + if (patch != null) { + major += "." + patch; + } + } + return major; + } + + /** + * Create Keyspace REST + * + * @param kspObject + * @param keyspaceName + * @return + * @throws Exception + */ + @POST + @Path("/{name}") + @ApiOperation(value = "Create Keyspace", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map createKeySpace( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonKeySpace kspObject, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("name") String keyspaceName, + @Context HttpServletResponse response) throws Exception { + Map resultMap = CachingUtil.verifyOnboarding(ns, userId, password); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (!resultMap.isEmpty()) { + return resultMap; + } + + resultMap = MusicCore.autheticateUser(ns, userId, password, keyspaceName, aid, + "createKeySpace"); + String newAid = null; + if (!resultMap.isEmpty()) { + if (resultMap.containsKey("aid")) { + newAid = (String) resultMap.get("aid"); + } else + return resultMap; + } + + String consistency = MusicUtil.EVENTUAL;// for now this needs only eventual + // consistency + + PreparedQueryObject queryObject = new PreparedQueryObject(); + boolean result = false; + long start = System.currentTimeMillis(); + Map replicationInfo = kspObject.getReplicationInfo(); + String repString = "{" + MusicUtil.jsonMaptoSqlString(replicationInfo, ",") + "}"; + queryObject.appendQueryString( + "CREATE KEYSPACE " + keyspaceName + " WITH replication = " + repString); + if (kspObject.getDurabilityOfWrites() != null) { + queryObject.appendQueryString( + " AND durable_writes = " + kspObject.getDurabilityOfWrites()); + } + + queryObject.appendQueryString(";"); + long end = System.currentTimeMillis(); + logger.info("Time taken for setting up query in create keyspace:" + (end - start)); + + try { + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + logger.debug("resulta = " + result); + } catch (Exception e) { + logger.error(e.getMessage()); + + } + logger.debug("result = " + result); + if (!result) { + resultMap.put("Status", String.valueOf(result)); + resultMap.put("Exception", "Keyspace already exists. Please contact admin."); + if (resultMap.get("uuid").equals("new")) { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString( + "DELETE FROM admin.keyspace_master where uuid = " + newAid); + queryObject.appendQueryString(";"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + resultMap.remove("aid"); + resultMap.remove("uuid"); + return resultMap; + + } else { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString( + "UPDATE admin.keyspace_master SET keyspace_name=?,password=?,is_api=null where uuid = ?;"); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), null)); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), newAid)); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + resultMap.remove("aid"); + resultMap.remove("uuid"); + return resultMap; + } + + } + try { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString("CREATE ROLE IF NOT EXISTS '" + userId + + "' WITH PASSWORD = '" + password + "' AND LOGIN = true;"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + if (result) { + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString("GRANT ALL PERMISSIONS on KEYSPACE " + keyspaceName + + " to " + userId); + queryObject.appendQueryString(";"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + } else { + resultMap.remove("uuid"); + resultMap.put("Exception", "Exception while creating user."); + return resultMap; + } + } catch (Exception e) { + logger.error(e.getMessage()); + } + resultMap.remove("uuid"); + if (CachingUtil.isAAFApplication(ns)) + resultMap.remove("aid"); + resultMap.put("Status", String.valueOf(result)); + return resultMap; + + } + + /** + * + * @param kspObject + * @param keyspaceName + * @return + * @throws Exception + */ + @DELETE + @Path("/{name}") + @ApiOperation(value = "Delete Keyspace", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map dropKeySpace( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonKeySpace kspObject, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("name") String keyspaceName, + @Context HttpServletResponse response) throws Exception { + Map resultMap = MusicCore.autheticateUser(ns, userId, password, + keyspaceName, aid, "dropKeySpace"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + + String consistency = MusicUtil.EVENTUAL;// for now this needs only eventual + // consistency + String appName = CachingUtil.getAppName(keyspaceName); + String uuid = CachingUtil.getUuidFromMusicCache(keyspaceName); + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "select count(*) as count from admin.keyspace_master where application_name=? allow filtering;"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), appName)); + Row row = MusicCore.get(pQuery).one(); + long count = row.getLong(0); + + if (count == 0) { + resultMap.put("Exception", "Keyspace not found. Please make sure keyspace exists."); + return resultMap; + } else if (count == 1) { + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "UPDATE admin.keyspace_master SET keyspace_name=?,password=?,is_api=null where uuid = ?;"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), + MusicUtil.DEFAULTKEYSPACENAME)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), null)); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + MusicCore.nonKeyRelatedPut(pQuery, consistency); + } else { + pQuery = new PreparedQueryObject(); + pQuery.appendQueryString("delete from admin.keyspace_master where uuid = ?"); + pQuery.addValue(MusicUtil.convertToActualDataType(DataType.uuid(), uuid)); + MusicCore.nonKeyRelatedPut(pQuery, consistency); + } + + PreparedQueryObject queryObject = new PreparedQueryObject(); + queryObject.appendQueryString("DROP KEYSPACE " + keyspaceName + ";"); + return new JsonResponse(MusicCore.nonKeyRelatedPut(queryObject, consistency), "", "") + .toMap(); + } + + /** + * + * @param tableObj + * @param keyspace + * @param tablename + * @return + * @throws Exception + */ + @POST + @Path("/{keyspace}/tables/{tablename}") + @ApiOperation(value = "Create Table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map createTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonTable tableObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception { + Map resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "createTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + String consistency = MusicUtil.EVENTUAL; + // for now this needs only eventual consistency + PreparedQueryObject queryObject = new PreparedQueryObject(); + boolean result = false; + // first read the information about the table fields + Map fields = tableObj.getFields(); + StringBuilder fieldsString = new StringBuilder("(vector_ts text,"); + int counter = 0; + String primaryKey; + for (Map.Entry entry : fields.entrySet()) { + fieldsString.append("" + entry.getKey() + " " + entry.getValue() + ""); + if (entry.getKey().equals("PRIMARY KEY")) { + primaryKey = entry.getValue().substring(entry.getValue().indexOf('(') + 1); + primaryKey = primaryKey.substring(0, primaryKey.indexOf(')')); + } + if (counter == fields.size() - 1) + fieldsString.append(")"); + else + fieldsString.append(","); + counter = counter + 1; + } + // information about the name-value style properties + Map propertiesMap = tableObj.getProperties(); + StringBuilder propertiesString = new StringBuilder(); + if (propertiesMap != null) { + counter = 0; + for (Map.Entry entry : propertiesMap.entrySet()) { + Object ot = entry.getValue(); + String value = ot + ""; + if (ot instanceof String) { + value = "'" + value + "'"; + } else if (ot instanceof Map) { + Map otMap = (Map) ot; + value = "{" + MusicUtil.jsonMaptoSqlString(otMap, ",") + "}"; + } + + propertiesString.append(entry.getKey() + "=" + value + ""); + if (counter != propertiesMap.size() - 1) + propertiesString.append(" AND "); + + counter = counter + 1; + } + } + + queryObject.appendQueryString("CREATE TABLE IF NOT EXISTS " + keyspace + "." + tablename + + " " + fieldsString); + + if (propertiesMap != null) + queryObject.appendQueryString(" WITH " + propertiesString); + + queryObject.appendQueryString(";"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); + + return new JsonResponse(result, "", "").toMap(); + } + + /** + * + * @param keyspace + * @param tablename + * @param fieldName + * @param info + * @throws Exception + */ + @POST + @Path("/{keyspace}/tables/{tablename}/index/{field}") + @ApiOperation(value = "Create Index", response = String.class) + @Produces(MediaType.APPLICATION_JSON) + public Map createIndex( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @ApiParam(value = "Field Name", + required = true) @PathParam("field") String fieldName, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "createIndex"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) + return resultMap; + MultivaluedMap rowParams = info.getQueryParameters(); + String indexName = ""; + if (rowParams.getFirst("index_name") != null) + indexName = rowParams.getFirst("index_name"); + PreparedQueryObject query = new PreparedQueryObject(); + query.appendQueryString("Create index " + indexName + " if not exists on " + keyspace + "." + + tablename + " (" + fieldName + ");"); + return new JsonResponse(MusicCore.nonKeyRelatedPut(query, "eventual"), "", "").toMap(); + + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @return + * @throws Exception + */ + @POST + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Insert Into Table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map insertIntoTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonInsert insObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception { + Map resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "insertIntoTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + ReturnType result = null; + Map valuesMap = insObj.getValues(); + PreparedQueryObject queryObject = new PreparedQueryObject(); + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + String primaryKeyName = tableInfo.getPrimaryKey().get(0).getName(); + StringBuilder fieldsString = new StringBuilder("(vector_ts,"); + String vectorTs = + String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + StringBuilder valueString = new StringBuilder("(" + "?" + ","); + queryObject.addValue(vectorTs); + int counter = 0; + String primaryKey = ""; + + for (Map.Entry entry : valuesMap.entrySet()) { + fieldsString.append("" + entry.getKey()); + Object valueObj = entry.getValue(); + if (primaryKeyName.equals(entry.getKey())) { + primaryKey = entry.getValue() + ""; + primaryKey = primaryKey.replace("'", "''"); + } + + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + + Object formattedValue = MusicUtil.convertToActualDataType(colType, valueObj); + valueString.append("?"); + queryObject.addValue(formattedValue); + + if (counter == valuesMap.size() - 1) { + fieldsString.append(")"); + valueString.append(")"); + } else { + fieldsString.append(","); + valueString.append(","); + } + counter = counter + 1; + } + + queryObject.appendQueryString("INSERT INTO " + keyspace + "." + tablename + " " + + fieldsString + " VALUES " + valueString); + + String ttl = insObj.getTtl(); + String timestamp = insObj.getTimestamp(); + + if ((ttl != null) && (timestamp != null)) { + logger.info("both there"); + queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?"); + queryObject.addValue(Integer.parseInt(ttl)); + queryObject.addValue(Long.parseLong(timestamp)); + } + + if ((ttl != null) && (timestamp == null)) { + logger.info("ONLY TTL there"); + queryObject.appendQueryString(" USING TTL ?"); + queryObject.addValue(Integer.parseInt(ttl)); + } + + if ((ttl == null) && (timestamp != null)) { + logger.info("ONLY timestamp there"); + queryObject.appendQueryString(" USING TIMESTAMP ?"); + queryObject.addValue(Long.parseLong(timestamp)); + } + + queryObject.appendQueryString(";"); + + String consistency = insObj.getConsistencyInfo().get("type"); + try { + if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) { + result = MusicCore.eventualPut(queryObject); + } else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = insObj.getConsistencyInfo().get("lockId"); + result = MusicCore.criticalPut(keyspace, tablename, primaryKey, queryObject, lockId, + null); + } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { + result = MusicCore.atomicPut(keyspace, tablename, primaryKey, queryObject, null); + + } + return (result != null) ? result.toMap() + : new ReturnType(ResultType.FAILURE, + "Null result - Please Contact admin").toMap(); + } catch (Exception ex) { + logger.error(ex.getMessage()); + return new ReturnType(ResultType.FAILURE, ex.getMessage()).toMap(); + } + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @PUT + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Update Table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map updateTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonUpdate updateObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "updateTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + long startTime = System.currentTimeMillis(); + String operationId = UUID.randomUUID().toString();// just for infoging + // purposes. + String consistency = updateObj.getConsistencyInfo().get("type"); + logger.info("--------------Music " + consistency + " update-" + operationId + + "-------------------------"); + // obtain the field value pairs of the update + + PreparedQueryObject queryObject = new PreparedQueryObject(); + Map valuesMap = updateObj.getValues(); + + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + String vectorTs = + String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); + StringBuilder fieldValueString = new StringBuilder("vector_ts=?,"); + queryObject.addValue(vectorTs); + int counter = 0; + for (Map.Entry entry : valuesMap.entrySet()) { + Object valueObj = entry.getValue(); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object valueString = MusicUtil.convertToActualDataType(colType, valueObj); + fieldValueString.append(entry.getKey() + "= ?"); + queryObject.addValue(valueString); + if (counter != valuesMap.size() - 1) + fieldValueString.append(","); + counter = counter + 1; + } + String ttl = updateObj.getTtl(); + String timestamp = updateObj.getTimestamp(); + + queryObject.appendQueryString("UPDATE " + keyspace + "." + tablename + " "); + if ((ttl != null) && (timestamp != null)) { + + logger.info("both there"); + queryObject.appendQueryString(" USING TTL ? AND TIMESTAMP ?"); + queryObject.addValue(Integer.parseInt(ttl)); + queryObject.addValue(Long.parseLong(timestamp)); + } + + if ((ttl != null) && (timestamp == null)) { + logger.info("ONLY TTL there"); + queryObject.appendQueryString(" USING TTL ?"); + queryObject.addValue(Integer.parseInt(ttl)); + } + + if ((ttl == null) && (timestamp != null)) { + logger.info("ONLY timestamp there"); + queryObject.appendQueryString(" USING TIMESTAMP ?"); + queryObject.addValue(Long.parseLong(timestamp)); + } + // get the row specifier + RowIdentifier rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject); + + queryObject.appendQueryString( + " SET " + fieldValueString + " WHERE " + rowId.rowIdString + ";"); + + // get the conditional, if any + Condition conditionInfo; + if (updateObj.getConditions() == null) + conditionInfo = null; + else {// to avoid parsing repeatedly, just send the select query to + // obtain row + String selectQuery = "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + + rowId.rowIdString + ";"; + conditionInfo = new MusicCore.Condition(updateObj.getConditions(), selectQuery); + } + + ReturnType operationResult = null; + long jsonParseCompletionTime = System.currentTimeMillis(); + + if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) + operationResult = MusicCore.eventualPut(queryObject); + else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = updateObj.getConsistencyInfo().get("lockId"); + operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, lockId, conditionInfo); + } + else if (consistency.equalsIgnoreCase("atomic_delete_lock")) { + // this function is mainly for the benchmarks + operationResult = MusicCore.atomicPutWithDeleteLock(keyspace, tablename, + rowId.primarKeyValue, queryObject, conditionInfo); + } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { + operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, conditionInfo); + } + long actualUpdateCompletionTime = System.currentTimeMillis(); + + long endTime = System.currentTimeMillis(); + String timingString = "Time taken in ms for Music " + consistency + " update-" + operationId + + ":" + "|total operation time:" + (endTime - startTime) + + "|json parsing time:" + (jsonParseCompletionTime - startTime) + + "|update time:" + (actualUpdateCompletionTime - jsonParseCompletionTime) + + "|"; + + if (operationResult != null && operationResult.getTimingInfo() != null) { + String lockManagementTime = operationResult.getTimingInfo(); + timingString = timingString + lockManagementTime; + } + logger.info(timingString); + return (operationResult != null) ? operationResult.toMap() + : new ReturnType(ResultType.FAILURE, "Null result - Please Contact admin") + .toMap(); + } + + /** + * + * @param delObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @DELETE + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Delete From table", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map deleteFromTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonDelete delObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "deleteFromTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder columnString = new StringBuilder(); + + int counter = 0; + ArrayList columnList = delObj.getColumns(); + if (columnList != null) { + for (String column : columnList) { + columnString.append(column); + if (counter != columnList.size() - 1) + columnString.append(","); + counter = counter + 1; + } + } + + // get the row specifier + RowIdentifier rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject); + String rowSpec = rowId.rowIdString.toString(); + + if ((columnList != null) && (!rowSpec.isEmpty())) { + queryObject.appendQueryString("DELETE " + columnString + " FROM " + keyspace + "." + + tablename + " WHERE " + rowSpec + ";"); + } + + if ((columnList == null) && (!rowSpec.isEmpty())) { + queryObject.appendQueryString("DELETE FROM " + keyspace + "." + tablename + " WHERE " + + rowSpec + ";"); + } + + if ((columnList != null) && (rowSpec.isEmpty())) { + queryObject.appendQueryString( + "DELETE " + columnString + " FROM " + keyspace + "." + rowSpec + ";"); + } + + // get the conditional, if any + Condition conditionInfo; + if (delObj.getConditions() == null) + conditionInfo = null; + else {// to avoid parsing repeatedly, just send the select query to + // obtain row + String selectQuery = "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + + rowId.rowIdString + ";"; + conditionInfo = new MusicCore.Condition(delObj.getConditions(), selectQuery); + } + + String consistency = delObj.getConsistencyInfo().get("type"); + + ReturnType operationResult = null; + + if (consistency.equalsIgnoreCase(MusicUtil.EVENTUAL)) + operationResult = MusicCore.eventualPut(queryObject); + else if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { + String lockId = delObj.getConsistencyInfo().get("lockId"); + operationResult = MusicCore.criticalPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, lockId, conditionInfo); + } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { + operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue, + queryObject, conditionInfo); + } + try { + return operationResult.toMap(); + } catch (NullPointerException e) { + return new ReturnType(ResultType.FAILURE, e.getMessage()).toMap(); + } + } + + /** + * + * @param tabObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @DELETE + @Path("/{keyspace}/tables/{tablename}") + @ApiOperation(value = "Drop Table", response = String.class) + + public Map dropTable( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonTable tabObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception { + Map resultMap = + MusicCore.autheticateUser(ns, userId, password, keyspace, aid, "dropTable"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + return resultMap; + } + String consistency = "eventual";// for now this needs only eventual + // consistency + PreparedQueryObject query = new PreparedQueryObject(); + query.appendQueryString("DROP TABLE IF EXISTS " + keyspace + "." + tablename + ";"); + return new JsonResponse(MusicCore.nonKeyRelatedPut(query, consistency), "", "").toMap(); + } + + /** + * + * @param selObj + * @param keyspace + * @param tablename + * @param info + * @return + */ + @PUT + @Path("/{keyspace}/tables/{tablename}/rows/criticalget") + @ApiOperation(value = "Select Critical", response = Map.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map> selectCritical( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + JsonInsert selObj, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map resultMap = MusicCore.autheticateUser(ns, userId, password, keyspace, + aid, "selectCritical"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + logger.error("Error while authentication... "); + HashMap tempMap = new HashMap<>(); + tempMap.putAll(resultMap); + Map> results = new HashMap<>(); + results.put("Result", tempMap); + return results; + } + String lockId = selObj.getConsistencyInfo().get("lockId"); + + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder rowSpec = new StringBuilder(); + + RowIdentifier rowId = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject); + + queryObject.appendQueryString( + "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowSpec + ";"); + + ResultSet results = null; + + String consistency = selObj.getConsistencyInfo().get("type"); + + if (consistency.equalsIgnoreCase("critical")) { + results = MusicCore.criticalGet(keyspace, tablename, rowId.primarKeyValue, queryObject, + lockId); + } else if (consistency.equalsIgnoreCase("atomic")) { + results = MusicCore.atomicGet(keyspace, tablename, rowId.primarKeyValue, queryObject); + } + + return MusicCore.marshallResults(results); + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/{keyspace}/tables/{tablename}/rows") + @ApiOperation(value = "Select All or Select Specivic", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map> select( + @ApiParam(value = "Major Version", + required = true) @PathParam("version") String version, + @ApiParam(value = "Minor Version", + required = false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value = "Patch Version", + required = false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value = "AID", required = true) @HeaderParam("aid") String aid, + @ApiParam(value = "Application namespace", + required = true) @HeaderParam("ns") String ns, + @ApiParam(value = "userId", + required = true) @HeaderParam("userId") String userId, + @ApiParam(value = "Password", + required = true) @HeaderParam("password") String password, + @ApiParam(value = "Keyspace Name", + required = true) @PathParam("keyspace") String keyspace, + @ApiParam(value = "Table Name", + required = true) @PathParam("tablename") String tablename, + @Context UriInfo info, @Context HttpServletResponse response) throws Exception { + Map resultMap = + MusicCore.autheticateUser(ns, userId, password, keyspace, aid, "select"); + response.addHeader(xLatestVersion, MusicUtil.getVersion()); + if (resultMap.containsKey("aid")) + resultMap.remove("aid"); + if (!resultMap.isEmpty()) { + logger.error("Error while authentication... "); + HashMap tempMap = new HashMap<>(); + tempMap.putAll(resultMap); + Map> results = new HashMap<>(); + results.put("Result", tempMap); + return results; + } + PreparedQueryObject queryObject = new PreparedQueryObject(); + + if (info.getQueryParameters().isEmpty())// select all + queryObject.appendQueryString("SELECT * FROM " + keyspace + "." + tablename + ";"); + else { + int limit = -1; // do not limit the number of results + queryObject = selectSpecificQuery(version, minorVersion, patchVersion, aid, ns, userId, + password, keyspace, tablename, info, limit); + } + ResultSet results = MusicCore.get(queryObject); + return MusicCore.marshallResults(results); + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @param limit + * @return + * @throws Exception + */ + public PreparedQueryObject selectSpecificQuery(String version, String minorVersion, + String patchVersion, String aid, String ns, String userId, String password, + String keyspace, String tablename, UriInfo info, int limit) { + + PreparedQueryObject queryObject = new PreparedQueryObject(); + StringBuilder rowIdString = getRowIdentifier(keyspace, tablename, info.getQueryParameters(), + queryObject).rowIdString; + + queryObject.appendQueryString( + "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowIdString); + + if (limit != -1) { + queryObject.appendQueryString(" LIMIT " + limit); + } + + queryObject.appendQueryString(";"); + return queryObject; + + } + + /** + * + * @param keyspace + * @param tablename + * @param rowParams + * @param queryObject + * @return + * @throws Exception + */ + private RowIdentifier getRowIdentifier(String keyspace, String tablename, + MultivaluedMap rowParams, PreparedQueryObject queryObject) { + StringBuilder rowSpec = new StringBuilder(); + int counter = 0; + TableMetadata tableInfo = MusicCore.returnColumnMetadata(keyspace, tablename); + StringBuilder primaryKey = new StringBuilder(); + for (MultivaluedMap.Entry> entry : rowParams.entrySet()) { + String keyName = entry.getKey(); + List valueList = entry.getValue(); + String indValue = valueList.get(0); + DataType colType = tableInfo.getColumn(entry.getKey()).getType(); + Object formattedValue = MusicUtil.convertToActualDataType(colType, indValue); + primaryKey.append(indValue); + rowSpec.append(keyName + "= ?"); + queryObject.addValue(formattedValue); + if (counter != rowParams.size() - 1) + rowSpec.append(" AND "); + counter = counter + 1; + } + return new RowIdentifier(primaryKey.toString(), rowSpec, queryObject); + } + +} diff --git a/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java b/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java new file mode 100644 index 00000000..8612b1fa --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java @@ -0,0 +1,209 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.onap.music.datastore.jsonobjects.JsonLeasedLock; +import org.onap.music.lockingservice.MusicLockState; +import org.onap.music.main.MusicCore; +import org.onap.music.main.MusicUtil; +import org.onap.music.response.jsonobjects.JsonLockResponse; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + + +@Path("/v{version: [0-9]+}/locks/") +@Api(value="Lock Api") +public class RestMusicLocksAPI { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicLocksAPI.class); + private static String xLatestVersion = "X-latestVersion"; + /** + * Puts the requesting process in the q for this lock. The corresponding + * node will be created in zookeeper if it did not already exist + * + * @param lockName + * @return + */ + + @POST + @Path("/create/{lockname}") + @ApiOperation(value = "Create Lock", + notes = "Puts the requesting process in the q for this lock." + + " The corresponding node will be created in zookeeper if it did not already exist." + + " Lock Name is the \"key\" of the form keyspaceName.tableName.rowId", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map createLockReference( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Boolean status = true; + String lockId = MusicCore.createLockReference(lockName); + if ( lockId == null ) { status = false; } + return new JsonLockResponse(status.toString(),"",lockId).toMap(); + } + + /** + * + * Checks if the node is in the top of the queue and hence acquires the lock + * + * @param lockId + * @return + */ + @GET + @Path("/acquire/{lockreference}") + @ApiOperation(value = "Aquire Lock", + notes = "Checks if the node is in the top of the queue and hence acquires the lock", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map accquireLock( + @ApiParam(value="Lock Reference",required=true) @PathParam("lockreference") String lockId, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); + Boolean lockStatus = MusicCore.acquireLock(lockName,lockId); + return new JsonLockResponse(lockStatus.toString(),"",lockId,lockStatus.toString(),"").toMap(); + } + + + + + @POST + @Path("/acquire-with-lease/{lockreference}") + @ApiOperation(value = "Aquire Lock with Lease", response = Map.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map accquireLockWithLease(JsonLeasedLock lockObj, + @ApiParam(value="Lock Reference",required=true) @PathParam("lockreference") String lockId, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); + String lockLeaseStatus = MusicCore.acquireLockWithLease(lockName, lockId, lockObj.getLeasePeriod()).toString(); + return new JsonLockResponse(lockLeaseStatus,"",lockName,lockLeaseStatus,"",String.valueOf(lockObj.getLeasePeriod())).toMap(); + } + + + @GET + @Path("/enquire/{lockname}") + @ApiOperation(value = "Get Lock Holder", + notes = "Gets the current Lock Holder", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map currentLockHolder( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + String who = MusicCore.whoseTurnIsIt(lockName); + String status = "true"; + String error = ""; + if ( who == null ) { + status = "false"; + error = "There was a problem getting the lock holder"; + } + return new JsonLockResponse(status,error,lockName,"",who).toMap(); + } + + @GET + @Path("/{lockname}") + @ApiOperation(value = "Lock State", + notes = "Returns current Lock State and Holder.", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map currentLockState( + @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + MusicLockState mls = MusicCore.getMusicLockState(lockName); + Map returnMap = null; + JsonLockResponse jsonResponse = new JsonLockResponse("false","",lockName); + if(mls == null) { + jsonResponse.setError(""); + jsonResponse.setMessage("No lock object created yet.."); + } else { + jsonResponse.setStatus("true"); + jsonResponse.setLockStatus(mls.getLockStatus().toString()); + jsonResponse.setLockHolder(mls.getLockHolder()); + } + return returnMap; + } + + /** + * + * deletes the process from the zk queue + * + * @param lockId + */ + @DELETE + @Path("/release/{lockreference}") + @ApiOperation(value = "Release Lock", + notes = "deletes the process from the zk queue", + response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map unLock(@PathParam("lockreference") String lockId, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + boolean voluntaryRelease = true; + MusicLockState mls = MusicCore.releaseLock(lockId,voluntaryRelease); + Map returnMap = null; + if ( mls.getLockStatus() == MusicLockState.LockStatus.UNLOCKED ) { + returnMap = new JsonLockResponse("Unlocked","","").toMap(); + } + if ( mls.getLockStatus() == MusicLockState.LockStatus.LOCKED) { + returnMap = new JsonLockResponse("Locked","","").toMap(); + } + return returnMap; + } + + /** + * + * @param lockName + */ + @DELETE + @Path("/delete/{lockname}") + @ApiOperation(value = "Delete Lock", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map deleteLock(@PathParam("lockname") String lockName, + @Context HttpServletResponse response){ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + MusicCore.deleteLock(lockName); + return new JsonLockResponse("true","","").toMap(); + } + +} diff --git a/src/main/java/org/onap/music/rest/RestMusicQAPI.java b/src/main/java/org/onap/music/rest/RestMusicQAPI.java new file mode 100755 index 00000000..3e92461c --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicQAPI.java @@ -0,0 +1,257 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.rest; + + + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.onap.music.datastore.jsonobjects.JsonDelete; +import org.onap.music.datastore.jsonobjects.JsonInsert; +import org.onap.music.datastore.jsonobjects.JsonTable; +import org.onap.music.datastore.jsonobjects.JsonUpdate; +import org.onap.music.main.MusicCore; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.music.datastore.PreparedQueryObject; +import com.datastax.driver.core.ResultSet; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; + +//@Path("/v{version: [0-9]+}/priorityq/") +@Path("/priorityq/") +@Api(value="Q Api") +public class RestMusicQAPI { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicQAPI.class); + + + /** + * + * @param tableObj + * @param keyspace + * @param tablename + * @throws Exception + */ + + @POST + @Path("/keyspaces/{keyspace}/{qname}") + @ApiOperation(value = "", response = Void.class) + @Consumes(MediaType.APPLICATION_JSON) + public Map createQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonTable tableObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().createTable(version,minorVersion,patchVersion,aid, ns, userId, password, tableObj, keyspace, tablename,response); + } + + /** + * + * @param insObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @POST + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "", response = Void.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map insertIntoQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonInsert insObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().insertIntoTable(version,minorVersion,patchVersion,aid, ns, userId, password, insObj, keyspace, tablename,response); + } + + /** + * + * @param updateObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @PUT + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map updateQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonUpdate updateObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().updateTable(version,minorVersion,patchVersion,aid, ns, userId, password, updateObj, keyspace, tablename, info,response); + } + + /** + * + * @param delObj + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @DELETE + @Path("/keyspaces/{keyspace}/{qname}/rows") + @ApiOperation(value = "", response = String.class) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Map deleteFromQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonDelete delObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().deleteFromTable(version,minorVersion,patchVersion,aid, ns, userId, password, delObj, keyspace, tablename, info,response); + } + + /** + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/keyspaces/{keyspace}/{qname}/peek") + @ApiOperation(value = "", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map> peek( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws Exception{ + int limit =1; //peek must return just the top row + PreparedQueryObject query = new RestMusicDataAPI().selectSpecificQuery(version,minorVersion,patchVersion,aid, ns, userId, password,keyspace,tablename,info,limit); + ResultSet results = MusicCore.get(query); + return MusicCore.marshallResults(results); + + } + + /** + * + * + * @param keyspace + * @param tablename + * @param info + * @return + * @throws Exception + */ + @GET + @Path("/keyspaces/{keyspace}/{qname}/filter") + @ApiOperation(value = "", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map> filter( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context UriInfo info) throws Exception{ + int limit =-1; + PreparedQueryObject query = new RestMusicDataAPI().selectSpecificQuery(version,minorVersion,patchVersion,aid, ns, userId, password,keyspace,tablename,info,limit); + ResultSet results = MusicCore.get(query); + return MusicCore.marshallResults(results); + } + + /** + * + * @param tabObj + * @param keyspace + * @param tablename + * @throws Exception + */ + @DELETE + @ApiOperation(value = "", response = Void.class) + @Path("/keyspaces/{keyspace}/{qname}") + public Map dropQ( + @ApiParam(value="Major Version",required=true) @PathParam("version") String version, + @ApiParam(value="Minor Version",required=false) @HeaderParam("X-minorVersion") String minorVersion, + @ApiParam(value="Patch Version",required=false) @HeaderParam("X-patchVersion") String patchVersion, + @ApiParam(value="AID",required=true) @HeaderParam("aid") String aid, + @ApiParam(value="Application namespace",required=true) @HeaderParam("ns") String ns, + @ApiParam(value="userId",required=true) @HeaderParam("userId") String userId, + @ApiParam(value="Password",required=true) @HeaderParam("password") String password, JsonTable tabObj, + @ApiParam(value="Key Space",required=true) @PathParam("keyspace") String keyspace, + @ApiParam(value="Table Name",required=true) @PathParam("tablename") String tablename, + @Context HttpServletResponse response) throws Exception{ + return new RestMusicDataAPI().dropTable(version,minorVersion,patchVersion,aid, ns, userId, password, tabObj, keyspace, tablename,response); + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicTestAPI.java b/src/main/java/org/onap/music/rest/RestMusicTestAPI.java new file mode 100644 index 00000000..6b6bc101 --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicTestAPI.java @@ -0,0 +1,67 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.onap.music.main.MusicUtil; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + + +@Path("/v{version: [0-9]+}/test") +@Api(value="Test Api") +public class RestMusicTestAPI { + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicTestAPI.class); + + /** + * Returns a test JSON. This will confirm that REST is working. + * @return + */ + @GET + @ApiOperation(value = "Get Test", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map> simpleTests( + @Context HttpServletResponse response) { + response.addHeader("X-latestVersion",MusicUtil.getVersion()); + Map> testMap = new HashMap<>(); + for(int i=0; i < 3; i++){ + HashMap innerMap = new HashMap<>(); + innerMap.put(i+"", i+1+""); + innerMap.put(i+1+"", i+2+""); + testMap.put(i+"", innerMap); + } + return testMap; + } +} diff --git a/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java b/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java new file mode 100644 index 00000000..924b0289 --- /dev/null +++ b/src/main/java/org/onap/music/rest/RestMusicVersionAPI.java @@ -0,0 +1,61 @@ +/* + * ============LICENSE_START========================================== + * org.onap.music + * =================================================================== + * Copyright (c) 2017 AT&T Intellectual Property + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============LICENSE_END============================================= + * ==================================================================== + */ +package org.onap.music.rest; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; + +import org.onap.music.response.jsonobjects.JsonResponse; +import org.onap.music.main.MusicUtil; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; + + +@Path("/v{version: [0-9]+}/version") +@Api(value="Version Api") +public class RestMusicVersionAPI { + + private static EELFLogger logger = EELFManager.getInstance().getLogger(RestMusicVersionAPI.class); + /** + * Get the version of MUSIC + * @return + */ + @GET + @ApiOperation(value = "Get Version", response = Map.class) + @Produces(MediaType.APPLICATION_JSON) + public Map version(@Context HttpServletResponse response) { + logger.info("Replying to request for MUSIC version with MUSIC:" + MusicUtil.getVersion()); + JsonResponse jResponse = new JsonResponse(true,"","MUSIC:" + MusicUtil.getVersion()); + response.addHeader("X-latestVersion",MusicUtil.getVersion()); + return jResponse.toMap(); + } +} \ No newline at end of file -- cgit 1.2.3-korg