From b6a14a0dc52c9ed43b035aff79831fc32fd52fe3 Mon Sep 17 00:00:00 2001 From: "Thomas Nelson Jr (arthurdent3)" Date: Fri, 23 Feb 2018 15:14:24 -0500 Subject: Changes related to Authentication and Atomic Authentication Fixes Atomic with delete added. Issue-ID: MUSIC-38,MUSIC-39 Change-Id: Ide2974a42ff8618d8a94ef7e8a1b0abde3630550 Signed-off-by: Thomas Nelson Jr (arthurdent3) --- pom.xml | 18 ++- .../org/onap/music/datastore/MusicDataStore.java | 8 +- src/main/java/org/onap/music/main/CachingUtil.java | 60 +++++++-- src/main/java/org/onap/music/main/MusicCore.java | 135 +++++++++++++-------- src/main/java/org/onap/music/main/MusicUtil.java | 2 + .../java/org/onap/music/rest/RestMusicBmAPI.java | 11 +- .../java/org/onap/music/rest/RestMusicDataAPI.java | 47 ++++--- .../org/onap/music/rest/RestMusicLocksAPI.java | 113 ++++++++++++++--- .../java/org/onap/music/rest/RestMusicTestAPI.java | 6 +- src/main/resources/Resources.properties | 3 +- .../org/onap/music/unittests/CassandraCQL.java | 73 +++++------ .../org/onap/music/unittests/TestMusicCore.java | 17 +-- version.properties | 2 +- 13 files changed, 332 insertions(+), 163 deletions(-) diff --git a/pom.xml b/pom.xml index b14b4f3a..683c56a4 100755 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ org.onap.music MUSIC war - 2.4.5 + 2.4.11 This is the MUSIC REST interface, packaged as a war file. @@ -39,7 +39,7 @@ UTF-8 - 1.9 + 1.17 2.26 2.0.1 3.0.0 @@ -172,7 +172,7 @@ ch.qos.logback logback-core - 1.1.3 + 1.2.3 @@ -185,7 +185,7 @@ ch.qos.logback logback-classic - 1.1.3 + 1.2.3 org.slf4j @@ -202,12 +202,18 @@ org.cassandraunit cassandra-unit - 3.3.0.1 + 3.3.0.2 org.apache.zookeeper zookeeper ${zookeeper.version} + + + org.slf4j + slf4j-log4j12 + + com.sun.jersey @@ -271,7 +277,7 @@ io.swagger swagger-jersey-jaxrs - 1.5.0 + 1.5.8 diff --git a/src/main/java/org/onap/music/datastore/MusicDataStore.java b/src/main/java/org/onap/music/datastore/MusicDataStore.java index 6ced1e48..e1ae5b08 100644 --- a/src/main/java/org/onap/music/datastore/MusicDataStore.java +++ b/src/main/java/org/onap/music/datastore/MusicDataStore.java @@ -45,6 +45,7 @@ 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.AlreadyExistsException; import com.datastax.driver.core.exceptions.NoHostAvailableException; /** @@ -322,7 +323,12 @@ public class MusicDataStore { ResultSet rs = session.execute(preparedInsert.bind(queryObject.getValues().toArray())); result = rs.wasApplied(); - } catch (Exception e) { + } + catch (AlreadyExistsException ae) { + logger.error(EELFLoggerDelegate.errorLogger, "Executing Session Failure for Request = " + + "[" + queryObject.getQuery() + "]" + " Reason = " + ae.getMessage()); + } + catch (Exception e) { logger.error(EELFLoggerDelegate.errorLogger, "Executing Session Failure for Request = " + "[" + queryObject.getQuery() + "]" + " Reason = " + e.getMessage()); throw new MusicServiceException("Executing Session Failure for Request = " + "[" diff --git a/src/main/java/org/onap/music/main/CachingUtil.java b/src/main/java/org/onap/music/main/CachingUtil.java index cd1d565d..4b2b4824 100755 --- a/src/main/java/org/onap/music/main/CachingUtil.java +++ b/src/main/java/org/onap/music/main/CachingUtil.java @@ -57,6 +57,7 @@ public class CachingUtil implements Runnable { private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CachingUtil.class); private static CacheAccess musicCache = JCS.getInstance("musicCache"); + private static CacheAccess musicLockCache = JCS.getInstance("musicLockCache"); private static CacheAccess> aafCache = JCS.getInstance("aafCache"); private static CacheAccess appNameCache = JCS.getInstance("appNameCache"); private static Map userAttempts = new HashMap<>(); @@ -76,7 +77,7 @@ public class CachingUtil implements Runnable { public void initializeAafCache() throws MusicServiceException { logger.info(EELFLoggerDelegate.applicationLogger,"Resetting and initializing AAF Cache..."); - String query = "SELECT application_name, keyspace_name, username, password FROM admin.keyspace_master WHERE is_api = ? allow filtering"; + String query = "SELECT uuid, application_name, keyspace_name, username, password FROM admin.keyspace_master WHERE is_api = ? allow filtering"; PreparedQueryObject pQuery = new PreparedQueryObject(); pQuery.appendQueryString(query); try { @@ -94,6 +95,7 @@ public class CachingUtil implements Runnable { String userId = row.getString("username"); String password = row.getString("password"); String keySpace = row.getString("application_name"); + String uuid = row.getUUID("uuid").toString(); try { userAttempts.put(nameSpace, 0); AAFResponse responseObj = triggerAAF(nameSpace, userId, password); @@ -102,6 +104,7 @@ public class CachingUtil implements Runnable { map.put(userId, password); aafCache.put(nameSpace, map); musicCache.put(nameSpace, keySpace); + musicLockCache.put(nameSpace, uuid); logger.debug("Cronjob: Cache Updated with AAF response for namespace " + nameSpace); } @@ -130,7 +133,7 @@ public class CachingUtil implements Runnable { String keySpace) throws Exception { if (aafCache.get(nameSpace) != null) { - if (!musicCache.get(nameSpace).equals(keySpace)) { + if (keySpace != null && !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.."); @@ -161,9 +164,12 @@ public class CachingUtil implements Runnable { AAFResponse responseObj = triggerAAF(nameSpace, userId, password); if (responseObj.getNs().size() > 0) { - if (responseObj.getNs().get(0).getAdmin().contains(userId)) - return true; - + if (responseObj.getNs().get(0).getAdmin().contains(userId)) { + //Map map = new HashMap<>(); + //map.put(userId, password); + //aafCache.put(nameSpace, map); + return true; + } } logger.info(EELFLoggerDelegate.applicationLogger,"Invalid user. Cache not updated"); return false; @@ -250,6 +256,43 @@ public class CachingUtil implements Runnable { resultMap.put("aid", uuid); return resultMap; } + + + public static Map authenticateAIDUserLock(String aid, String nameSpace) + throws Exception { + Map resultMap = new HashMap<>(); + String uuid = null; + + if (musicLockCache.get(nameSpace) == null) { + PreparedQueryObject pQuery = new PreparedQueryObject(); + pQuery.appendQueryString( + "SELECT uuid from admin.keyspace_master where application_name = '" + + nameSpace + "' allow filtering"); + Row rs = MusicCore.get(pQuery).one(); + try { + uuid = rs.getUUID("uuid").toString(); + musicLockCache.put(nameSpace, uuid); + } catch (Exception e) { + logger.error("Exception occured during uuid retrieval from DB." + e.getMessage()); + resultMap.put("Exception", "Unauthorized operation. Check AID and Namespace. "); + return resultMap; + } + if (!musicLockCache.get(nameSpace).toString().equals(aid)) { + resultMap.put("Exception Message", + "Unauthorized operation. Invalid AID for the Namespace"); + return resultMap; + } + } else if (musicLockCache.get(nameSpace) != null + && !musicLockCache.get(nameSpace).toString().equals(aid)) { + resultMap.put("Exception Message", + "Unauthorized operation. Invalid AID for the Namespace"); + return resultMap; + } + return resultMap; + } + + + public static void updateMusicCache(String aid, String keyspace) { logger.info("Updating musicCache for keyspace " + keyspace + " with aid " + aid); @@ -346,14 +389,15 @@ public class CachingUtil implements Runnable { } PreparedQueryObject queryObject = new PreparedQueryObject(); queryObject.appendQueryString( - "select * from admin.keyspace_master where application_name=? and username=? allow filtering"); + "select * from admin.keyspace_master where application_name = ? and username = ? and password = ? allow filtering"); queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), ns)); queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId)); + queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), password)); Row rs = MusicCore.get(queryObject).one(); if (rs == null) { - logger.error(EELFLoggerDelegate.errorLogger,"Namespace and UserId doesn't match. namespace: "+ns+" and userId: "+userId); + logger.error(EELFLoggerDelegate.errorLogger,"Namespace, UserId and password doesn't match. namespace: "+ns+" and userId: "+userId); - resultMap.put("Exception", "Namespace and UserId doesn't match. namespace: "+ns+" and userId: "+userId); + resultMap.put("Exception", "Namespace, UserId and password doesn't match. namespace: "+ns+" and userId: "+userId); } else { boolean is_aaf = rs.getBool("is_aaf"); String keyspace = rs.getString("keyspace_name"); diff --git a/src/main/java/org/onap/music/main/MusicCore.java b/src/main/java/org/onap/music/main/MusicCore.java index 96056160..3d5cf4e5 100644 --- a/src/main/java/org/onap/music/main/MusicCore.java +++ b/src/main/java/org/onap/music/main/MusicCore.java @@ -385,44 +385,7 @@ public class MusicCore { } - /** - * 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(EELFLoggerDelegate.applicationLogger,"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(EELFLoggerDelegate.applicationLogger,"unable to acquire lock, id " + lockId); - deleteLock(key); - return lockAcqResult; - } - } + /** * @@ -515,6 +478,10 @@ public class MusicCore { logger.info(EELFLoggerDelegate.applicationLogger,"Time taken to release lock:" + (end - start) + " ms"); return mls; } + + public static void voluntaryReleaseLock(String lockId) throws MusicLockingException{ + getLockingServiceHandle().unlockAndDeleteId(lockId); + } /** * @@ -734,22 +701,63 @@ public class MusicCore { * @param primaryKey primary key value * @param queryObject query object containing prepared query and values * @return ReturnType + * @throws MusicLockingException */ public static ReturnType atomicPut(String keyspaceName, String tableName, String primaryKey, - PreparedQueryObject queryObject, Condition conditionInfo) { + PreparedQueryObject queryObject, Condition conditionInfo) throws MusicLockingException { + + long start = System.currentTimeMillis(); + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long lockCreationTime = System.currentTimeMillis(); + ReturnType lockAcqResult = acquireLock(key, lockId); + long lockAcqTime = System.currentTimeMillis(); + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info(EELFLoggerDelegate.applicationLogger,"acquired lock with id " + lockId); + ReturnType criticalPutResult = criticalPut(keyspaceName, tableName, primaryKey, + queryObject, lockId, conditionInfo); + long criticalPutTime = System.currentTimeMillis(); + voluntaryReleaseLock(lockId); + 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(EELFLoggerDelegate.applicationLogger,"unable to acquire lock, id " + lockId); + destroyLockRef(lockId); + return lockAcqResult; + } + } + + /** + * 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 + * @throws MusicLockingException + */ + public static ReturnType atomicPutWithDeleteLock(String keyspaceName, String tableName, + String primaryKey, PreparedQueryObject queryObject, Condition conditionInfo) throws MusicLockingException { + 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); + ReturnType lockAcqResult = acquireLock(key, lockId); long lockAcqTime = System.currentTimeMillis(); if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { logger.info(EELFLoggerDelegate.applicationLogger,"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) @@ -760,10 +768,12 @@ public class MusicCore { return criticalPutResult; } else { logger.info(EELFLoggerDelegate.applicationLogger,"unable to acquire lock, id " + lockId); - destroyLockRef(lockId); + deleteLock(key); return lockAcqResult; } } + + /** @@ -775,25 +785,48 @@ public class MusicCore { * @param queryObject query object containing prepared query and values * @return ResultSet * @throws MusicServiceException + * @throws MusicLockingException */ public static ResultSet atomicGet(String keyspaceName, String tableName, String primaryKey, - PreparedQueryObject queryObject) throws MusicServiceException { + PreparedQueryObject queryObject) throws MusicServiceException, MusicLockingException { String key = keyspaceName + "." + tableName + "." + primaryKey; String lockId = createLockReference(key); long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); - ReturnType lockAcqResult = acquireLockWithLease(key, lockId, leasePeriod); + ReturnType lockAcqResult = acquireLock(key, lockId); if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { logger.info(EELFLoggerDelegate.applicationLogger,"acquired lock with id " + lockId); ResultSet result = criticalGet(keyspaceName, tableName, primaryKey, queryObject, lockId); - boolean voluntaryRelease = true; - releaseLock(lockId, voluntaryRelease); + voluntaryReleaseLock(lockId); return result; } else { + destroyLockRef(lockId); logger.info(EELFLoggerDelegate.applicationLogger,"unable to acquire lock, id " + lockId); return null; } } + + public static ResultSet atomicGetWithDeleteLock(String keyspaceName, String tableName, String primaryKey, + PreparedQueryObject queryObject) throws MusicServiceException, MusicLockingException { + String key = keyspaceName + "." + tableName + "." + primaryKey; + String lockId = createLockReference(key); + long leasePeriod = MusicUtil.getDefaultLockLeasePeriod(); + + ReturnType lockAcqResult = acquireLock(key, lockId); + + if (lockAcqResult.getResult().equals(ResultType.SUCCESS)) { + logger.info(EELFLoggerDelegate.applicationLogger, "acquired lock with id " + lockId); + ResultSet result = criticalGet(keyspaceName, tableName, primaryKey, queryObject, lockId); + deleteLock(key); + return result; + } else { + deleteLock(key); + logger.info(EELFLoggerDelegate.applicationLogger, "unable to acquire lock, id " + lockId); + return null; + } + } + + /** * authenticate user logic @@ -822,7 +855,13 @@ public class MusicCore { resultMap.put("Exception", "Aid is mandatory for nonAAF applications "); return resultMap; } - resultMap = CachingUtil.authenticateAIDUser(aid, keyspace); + if(operation.contains("Lock")) { + resultMap = CachingUtil.authenticateAIDUserLock(aid, nameSpace); + } + else { + resultMap = CachingUtil.authenticateAIDUser(aid, keyspace); + } + if (!resultMap.isEmpty()) return resultMap; } @@ -837,7 +876,7 @@ public class MusicCore { if (isAAF && nameSpace != null && userId != null && password != null) { boolean isValid = true; try { - isValid = CachingUtil.authenticateAAFUser(nameSpace, userId, password, keyspace); + isValid = CachingUtil.authenticateAAFUser(nameSpace, userId, password, keyspace); } catch (Exception e) { logger.error(EELFLoggerDelegate.errorLogger,"Got exception while AAF authentication for namespace " + nameSpace); resultMap.put("Exception", e.getMessage()); diff --git a/src/main/java/org/onap/music/main/MusicUtil.java b/src/main/java/org/onap/music/main/MusicUtil.java index 47e23973..317f2560 100755 --- a/src/main/java/org/onap/music/main/MusicUtil.java +++ b/src/main/java/org/onap/music/main/MusicUtil.java @@ -66,6 +66,8 @@ public class MusicUtil { public static final String ATOMIC = "atomic"; public static final String EVENTUAL = "eventual"; public static final String CRITICAL = "critical"; + public static final String ATOMICDELETELOCK = "atomic_delete_lock"; + public static final String DEFAULTKEYSPACENAME = "TBD"; private static String cassName = "cassandra"; private static String cassPwd = "cassandra"; diff --git a/src/main/java/org/onap/music/rest/RestMusicBmAPI.java b/src/main/java/org/onap/music/rest/RestMusicBmAPI.java index 8f62f9c1..55eb47f2 100644 --- a/src/main/java/org/onap/music/rest/RestMusicBmAPI.java +++ b/src/main/java/org/onap/music/rest/RestMusicBmAPI.java @@ -31,25 +31,18 @@ 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.eelf.logging.EELFLoggerDelegate; -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; +//import io.swagger.annotations.ApiOperation; +//import io.swagger.annotations.ApiParam; /* * These are functions created purely for benchmarking purposes. Commented out Swagger - This should diff --git a/src/main/java/org/onap/music/rest/RestMusicDataAPI.java b/src/main/java/org/onap/music/rest/RestMusicDataAPI.java index e16cc373..f1d88efa 100755 --- a/src/main/java/org/onap/music/rest/RestMusicDataAPI.java +++ b/src/main/java/org/onap/music/rest/RestMusicDataAPI.java @@ -36,8 +36,6 @@ 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; @@ -93,6 +91,7 @@ public class RestMusicDataAPI { private class RowIdentifier { public String primarKeyValue; public StringBuilder rowIdString; + @SuppressWarnings("unused") public PreparedQueryObject queryObject;// the string with all the row // identifiers separated by AND @@ -104,6 +103,7 @@ public class RestMusicDataAPI { } } + @SuppressWarnings("unused") private String buildVersion(String major, String minor, String patch) { if (minor != null) { major += "." + minor; @@ -187,12 +187,14 @@ public class RestMusicDataAPI { logger.error(EELFLoggerDelegate.errorLogger, "resulta = " + result); } catch (Exception e) { logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); + resultMap.put("Exception", "Couldn't create keyspace. Please make sure all the information is correct."); } logger.debug("result = " + result); if (!result) { resultMap.put("Status", String.valueOf(result)); - resultMap.put("Exception", "Keyspace already exists. Please contact admin."); + if(!resultMap.containsKey("Exception")) + resultMap.put("Exception", "Keyspace already exists. Please contact admin."); if (resultMap.get("uuid").equals("new")) { queryObject = new PreparedQueryObject(); queryObject.appendQueryString( @@ -206,10 +208,9 @@ public class RestMusicDataAPI { } else { queryObject = new PreparedQueryObject(); queryObject.appendQueryString( - "UPDATE admin.keyspace_master SET keyspace_name=?,password=?,is_api=null where uuid = ?;"); + "UPDATE admin.keyspace_master SET keyspace_name=? 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"); @@ -223,17 +224,11 @@ public class RestMusicDataAPI { 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; - } + queryObject = new PreparedQueryObject(); + queryObject.appendQueryString("GRANT ALL PERMISSIONS on KEYSPACE " + keyspaceName + + " to '" + userId + "'"); + queryObject.appendQueryString(";"); + result = MusicCore.nonKeyRelatedPut(queryObject, consistency); } catch (Exception e) { logger.error(EELFLoggerDelegate.errorLogger, e.getMessage()); } @@ -393,6 +388,7 @@ public class RestMusicDataAPI { if (ot instanceof String) { value = "'" + value + "'"; } else if (ot instanceof Map) { + @SuppressWarnings("unchecked") Map otMap = (Map) ot; value = "{" + MusicUtil.jsonMaptoSqlString(otMap, ",") + "}"; } @@ -588,6 +584,10 @@ public class RestMusicDataAPI { } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { result = MusicCore.atomicPut(keyspace, tablename, primaryKey, queryObject, null); + } + else if (consistency.equalsIgnoreCase(MusicUtil.ATOMICDELETELOCK)) { + result = MusicCore.atomicPutWithDeleteLock(keyspace, tablename, primaryKey, queryObject, null); + } return (result != null) ? result.toMap() : new ReturnType(ResultType.FAILURE, @@ -862,6 +862,10 @@ public class RestMusicDataAPI { operationResult = MusicCore.atomicPut(keyspace, tablename, rowId.primarKeyValue, queryObject, conditionInfo); } + else if (consistency.equalsIgnoreCase(MusicUtil.ATOMICDELETELOCK)) { + operationResult = MusicCore.atomicPutWithDeleteLock(keyspace, tablename, rowId.primarKeyValue, + queryObject, conditionInfo); + } try { return operationResult.toMap(); } catch (NullPointerException e) { @@ -970,7 +974,6 @@ public class RestMusicDataAPI { String lockId = selObj.getConsistencyInfo().get("lockId"); PreparedQueryObject queryObject = new PreparedQueryObject(); - StringBuilder rowSpec = new StringBuilder(); RowIdentifier rowId = null; try { @@ -979,18 +982,22 @@ public class RestMusicDataAPI { return MusicUtil.setErrorResponse(ex); } queryObject.appendQueryString( - "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowSpec + ";"); + "SELECT * FROM " + keyspace + "." + tablename + " WHERE " + rowId.rowIdString + ";"); ResultSet results = null; String consistency = selObj.getConsistencyInfo().get("type"); - if (consistency.equalsIgnoreCase("critical")) { + if (consistency.equalsIgnoreCase(MusicUtil.CRITICAL)) { results = MusicCore.criticalGet(keyspace, tablename, rowId.primarKeyValue, queryObject, lockId); - } else if (consistency.equalsIgnoreCase("atomic")) { + } else if (consistency.equalsIgnoreCase(MusicUtil.ATOMIC)) { results = MusicCore.atomicGet(keyspace, tablename, rowId.primarKeyValue, queryObject); } + + else if (consistency.equalsIgnoreCase(MusicUtil.ATOMICDELETELOCK)) { + results = MusicCore.atomicGetWithDeleteLock(keyspace, tablename, rowId.primarKeyValue, queryObject); + } return MusicCore.marshallResults(results); } diff --git a/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java b/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java index 5f28f447..e565e829 100644 --- a/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java +++ b/src/main/java/org/onap/music/rest/RestMusicLocksAPI.java @@ -27,6 +27,7 @@ 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.Path; import javax.ws.rs.PathParam; @@ -37,16 +38,11 @@ import javax.ws.rs.core.MediaType; import org.onap.music.datastore.jsonobjects.JsonLeasedLock; import org.onap.music.eelf.logging.EELFLoggerDelegate; import org.onap.music.lockingservice.MusicLockState; -import org.onap.music.lockingservice.MusicLockState.LockStatus; 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.response.jsonobjects.JsonLockResponse; -import org.powermock.core.spi.testresult.Result; - -import com.att.eelf.configuration.EELFLogger; -import com.att.eelf.configuration.EELFManager; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -57,7 +53,8 @@ import io.swagger.annotations.ApiParam; @Api(value="Lock Api") public class RestMusicLocksAPI { - private EELFLoggerDelegate logger =EELFLoggerDelegate.getLogger(RestMusicLocksAPI.class); + @SuppressWarnings("unused") + private EELFLoggerDelegate logger =EELFLoggerDelegate.getLogger(RestMusicLocksAPI.class); private static String xLatestVersion = "X-latestVersion"; /** @@ -66,6 +63,7 @@ public class RestMusicLocksAPI { * * @param lockName * @return + * @throws Exception */ @POST @@ -78,8 +76,20 @@ public class RestMusicLocksAPI { @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()); + @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, + @Context HttpServletResponse response) throws Exception{ + response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Map resultMap = MusicCore.autheticateUser(ns, userId, password, null, aid, + "createLockReference"); + if (!resultMap.isEmpty()) { + return resultMap; + } ResultType status = ResultType.SUCCESS; String lockId = MusicCore.createLockReference(lockName); if (lockId == null) { status = ResultType.FAILURE; } @@ -92,6 +102,7 @@ public class RestMusicLocksAPI { * * @param lockId * @return + * @throws Exception */ @GET @Path("/acquire/{lockreference}") @@ -101,8 +112,20 @@ public class RestMusicLocksAPI { @Produces(MediaType.APPLICATION_JSON) public Map accquireLock( @ApiParam(value="Lock Reference",required=true) @PathParam("lockreference") String lockId, - @Context HttpServletResponse response){ + @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, + @Context HttpServletResponse response) throws Exception{ response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Map resultMap = MusicCore.autheticateUser(ns, userId, password, null, aid, + "accquireLock"); + if (!resultMap.isEmpty()) { + return resultMap; + } String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); ReturnType lockStatus = MusicCore.acquireLock(lockName,lockId); return new JsonLockResponse(lockStatus.getResult()).setLock(lockId) @@ -119,8 +142,20 @@ public class RestMusicLocksAPI { @Produces(MediaType.APPLICATION_JSON) public Map accquireLockWithLease(JsonLeasedLock lockObj, @ApiParam(value="Lock Reference",required=true) @PathParam("lockreference") String lockId, - @Context HttpServletResponse response){ + @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, + @Context HttpServletResponse response) throws Exception{ response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Map resultMap = MusicCore.autheticateUser(ns, userId, password, null, aid, + "accquireLockWithLease"); + if (!resultMap.isEmpty()) { + return resultMap; + } String lockName = lockId.substring(lockId.indexOf('$')+1, lockId.lastIndexOf('$')); ReturnType lockLeaseStatus = MusicCore.acquireLockWithLease(lockName, lockId, lockObj.getLeasePeriod()); return new JsonLockResponse(lockLeaseStatus.getResult()).setLock(lockName) @@ -137,8 +172,20 @@ public class RestMusicLocksAPI { @Produces(MediaType.APPLICATION_JSON) public Map currentLockHolder( @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, - @Context HttpServletResponse response){ + @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, + @Context HttpServletResponse response) throws Exception{ response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Map resultMap = MusicCore.autheticateUser(ns, userId, password, null, aid, + "currentLockHolder"); + if (!resultMap.isEmpty()) { + return resultMap; + } String who = MusicCore.whoseTurnIsIt(lockName); ResultType status = ResultType.SUCCESS; String error = ""; @@ -158,8 +205,20 @@ public class RestMusicLocksAPI { @Produces(MediaType.APPLICATION_JSON) public Map currentLockState( @ApiParam(value="Lock Name",required=true) @PathParam("lockname") String lockName, - @Context HttpServletResponse response){ + @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, + @Context HttpServletResponse response) throws Exception{ response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Map resultMap = MusicCore.autheticateUser(ns, userId, password, null, aid, + "currentLockState"); + if (!resultMap.isEmpty()) { + return resultMap; + } MusicLockState mls = MusicCore.getMusicLockState(lockName); Map returnMap = null; JsonLockResponse jsonResponse = new JsonLockResponse(ResultType.FAILURE).setLock(lockName); @@ -179,6 +238,7 @@ public class RestMusicLocksAPI { * deletes the process from the zk queue * * @param lockId + * @throws Exception */ @DELETE @Path("/release/{lockreference}") @@ -187,8 +247,20 @@ public class RestMusicLocksAPI { response = Map.class) @Produces(MediaType.APPLICATION_JSON) public Map unLock(@PathParam("lockreference") String lockId, - @Context HttpServletResponse response){ + @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, + @Context HttpServletResponse response) throws Exception{ response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Map resultMap = MusicCore.autheticateUser(ns, userId, password, null, aid, + "unLock"); + if (!resultMap.isEmpty()) { + return resultMap; + } boolean voluntaryRelease = true; MusicLockState mls = MusicCore.releaseLock(lockId,voluntaryRelease); Map returnMap = null; @@ -206,14 +278,27 @@ public class RestMusicLocksAPI { /** * * @param lockName + * @throws Exception */ @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){ + @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, + @Context HttpServletResponse response) throws Exception{ response.addHeader(xLatestVersion,MusicUtil.getVersion()); + Map resultMap = MusicCore.autheticateUser(ns, userId, password, null, aid, + "deleteLock"); + if (!resultMap.isEmpty()) { + return resultMap; + } MusicCore.deleteLock(lockName); return new JsonLockResponse(ResultType.SUCCESS).toMap(); } diff --git a/src/main/java/org/onap/music/rest/RestMusicTestAPI.java b/src/main/java/org/onap/music/rest/RestMusicTestAPI.java index f606eb8d..287fa176 100644 --- a/src/main/java/org/onap/music/rest/RestMusicTestAPI.java +++ b/src/main/java/org/onap/music/rest/RestMusicTestAPI.java @@ -34,9 +34,6 @@ import javax.ws.rs.core.MediaType; import org.onap.music.eelf.logging.EELFLoggerDelegate; 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; @@ -45,7 +42,8 @@ import io.swagger.annotations.ApiOperation; @Api(value="Test Api") public class RestMusicTestAPI { - private EELFLoggerDelegate logger =EELFLoggerDelegate.getLogger(RestMusicTestAPI.class); + @SuppressWarnings("unused") + private EELFLoggerDelegate logger =EELFLoggerDelegate.getLogger(RestMusicTestAPI.class); /** * Returns a test JSON. This will confirm that REST is working. diff --git a/src/main/resources/Resources.properties b/src/main/resources/Resources.properties index 7ee7baae..72269cb8 100644 --- a/src/main/resources/Resources.properties +++ b/src/main/resources/Resources.properties @@ -46,4 +46,5 @@ UNSUPPORTED_LOGGING_FRAMEWORK=\ EELF0006E|\ An unsupported logging framework is bound to SLF4J. |\ Verify your logging frameworks.|\ - An unsupported logging framework is bound to SLF4J. \ No newline at end of file + An unsupported logging framework is bound to SLF4J. + diff --git a/src/test/java/org/onap/music/unittests/CassandraCQL.java b/src/test/java/org/onap/music/unittests/CassandraCQL.java index cbca37b2..ed8f13f4 100644 --- a/src/test/java/org/onap/music/unittests/CassandraCQL.java +++ b/src/test/java/org/onap/music/unittests/CassandraCQL.java @@ -19,7 +19,9 @@ * ============LICENSE_END============================================= * ==================================================================== */ + package org.onap.music.unittests; + /** * @author srupane * @@ -37,42 +39,43 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.NoHostAvailableException; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.thrift.transport.TTransportException; import org.cassandraunit.utils.EmbeddedCassandraServerHelper; import org.onap.music.datastore.MusicDataStore; import org.onap.music.datastore.PreparedQueryObject; -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.NoHostAvailableException; public class CassandraCQL { public static final String createKeySpace = - "CREATE KEYSPACE IF NOT EXISTS testCassa WITH replication = {'class':'SimpleStrategy','replication_factor':1} AND durable_writes = true;"; + "CREATE KEYSPACE IF NOT EXISTS testCassa WITH replication = " + +"{'class':'SimpleStrategy','replication_factor':1} AND durable_writes = true;"; public static final String dropKeyspace = "DROP KEYSPACE IF EXISTS testCassa"; public static final String createTableEmployees = "CREATE TABLE IF NOT EXISTS testCassa.employees " - + "(vector_ts text,emp_id uuid,emp_name text,emp_salary varint,address Map,PRIMARY KEY (emp_name)) " - + "WITH comment='Financial Info of employees' " - + "AND compression={'sstable_compression':'DeflateCompressor','chunk_length_kb':64} " - + "AND compaction={'class':'SizeTieredCompactionStrategy','min_threshold':6};"; + + "(vector_ts text,empId uuid,empName text,empSalary varint,address Map,PRIMARY KEY (empName)) " + + "WITH comment='Financial Info of employees' " + + "AND compression={'sstable_compression':'DeflateCompressor','chunk_length_kb':64} " + + "AND compaction={'class':'SizeTieredCompactionStrategy','min_threshold':6};"; public static final String insertIntoTablePrepared1 = - "INSERT INTO testCassa.employees (vector_ts,emp_id,emp_name,emp_salary) VALUES (?,?,?,?); "; + "INSERT INTO testCassa.employees (vector_ts,empId,empName,empSalary) VALUES (?,?,?,?); "; public static final String insertIntoTablePrepared2 = - "INSERT INTO testCassa.employees (vector_ts,emp_id,emp_name,emp_salary,address) VALUES (?,?,?,?,?);"; + "INSERT INTO testCassa.employees (vector_ts,empId,empName,empSalary,address) VALUES (?,?,?,?,?);"; public static final String selectALL = "SELECT * FROM testCassa.employees;"; public static final String selectSpecific = - "SELECT * FROM testCassa.employees WHERE emp_name= ?;"; + "SELECT * FROM testCassa.employees WHERE empName= ?;"; public static final String updatePreparedQuery = - "UPDATE testCassa.employees SET vector_ts=?,address= ? WHERE emp_name= ?;"; + "UPDATE testCassa.employees SET vector_ts=?,address= ? WHERE empName= ?;"; public static final String deleteFromTable = " "; @@ -85,13 +88,13 @@ public class CassandraCQL { List preppreparedInsertValues1 = new ArrayList<>(); String vectorTs = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); - UUID emp_id = UUID.fromString("abc66ccc-d857-4e90-b1e5-df98a3d40cd6"); - BigInteger emp_salary = BigInteger.valueOf(23443); - String emp_name = "Mr Test one"; + UUID empId = UUID.fromString("abc66ccc-d857-4e90-b1e5-df98a3d40cd6"); + BigInteger empSalary = BigInteger.valueOf(23443); + String empName = "Mr Test one"; preppreparedInsertValues1.add(vectorTs); - preppreparedInsertValues1.add(emp_id); - preppreparedInsertValues1.add(emp_name); - preppreparedInsertValues1.add(emp_salary); + preppreparedInsertValues1.add(empId); + preppreparedInsertValues1.add(empName); + preppreparedInsertValues1.add(empSalary); return preppreparedInsertValues1; } @@ -100,14 +103,14 @@ public class CassandraCQL { List preparedInsertValues2 = new ArrayList<>(); String vectorTs = String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); - UUID emp_id = UUID.fromString("abc434cc-d657-4e90-b4e5-df4223d40cd6"); - BigInteger emp_salary = BigInteger.valueOf(45655); - String emp_name = "Mr Test two"; + UUID empId = UUID.fromString("abc434cc-d657-4e90-b4e5-df4223d40cd6"); + BigInteger empSalary = BigInteger.valueOf(45655); + String empName = "Mr Test two"; Map address = new HashMap<>(); preparedInsertValues2.add(vectorTs); - preparedInsertValues2.add(emp_id); - preparedInsertValues2.add(emp_name); - preparedInsertValues2.add(emp_salary); + preparedInsertValues2.add(empId); + preparedInsertValues2.add(empName); + preparedInsertValues2.add(empSalary); address.put("Street", "1 some way"); address.put("City", "Some town"); preparedInsertValues2.add(address); @@ -121,33 +124,33 @@ public class CassandraCQL { String.valueOf(Thread.currentThread().getId() + System.currentTimeMillis()); Map address = new HashMap<>(); preparedUpdateValues.add(vectorTs); - String emp_name = "Mr Test one"; + String empName = "Mr Test one"; address.put("Street", "101 Some Way"); address.put("City", "New York"); preparedUpdateValues.add(address); - preparedUpdateValues.add(emp_name); + preparedUpdateValues.add(empName); return preparedUpdateValues; } // Generate Different Prepared Query Objects /** - * Query Object for Get + * Query Object for Get. * * @return */ public static PreparedQueryObject setPreparedGetQuery() { PreparedQueryObject queryObject = new PreparedQueryObject(); - String emp_name1 = "Mr Test one"; + String empName1 = "Mr Test one"; queryObject.appendQueryString(selectSpecific); - queryObject.addValue(emp_name1); + queryObject.addValue(empName1); return queryObject; } /** - * Query Object 1 for Insert + * Query Object 1 for Insert. * - * @return + * @return {@link PreparedQueryObject} */ public static PreparedQueryObject setPreparedInsertQueryObject1() { @@ -164,9 +167,9 @@ public class CassandraCQL { } /** - * Query Object 2 for Insert + * Query Object 2 for Insert. * - * @return + * @return {@link PreparedQueryObject} */ public static PreparedQueryObject setPreparedInsertQueryObject2() { @@ -183,9 +186,9 @@ public class CassandraCQL { } /** - * Query Object for Update + * Query Object for Update. * - * @return + * @return {@link PreparedQueryObject} */ public static PreparedQueryObject setPreparedUpdateQueryObject() { diff --git a/src/test/java/org/onap/music/unittests/TestMusicCore.java b/src/test/java/org/onap/music/unittests/TestMusicCore.java index ed328c26..2deb3496 100644 --- a/src/test/java/org/onap/music/unittests/TestMusicCore.java +++ b/src/test/java/org/onap/music/unittests/TestMusicCore.java @@ -139,7 +139,7 @@ public class TestMusicCore { @Test public void testAcquireLockifLockRefDoesntExist() { - Mockito.when(mLockHandle.lockIdExists("bs1")).thenReturn(false); + Mockito.when(mLockHandle.lockIdExists("bs1")).thenReturn(false); ReturnType lock = MusicCore.acquireLock("ks1.ts1", "bs1"); assertEquals(lock.getResult(), ResultType.FAILURE); assertEquals(lock.getMessage(), "Lockid doesn't exist"); @@ -351,19 +351,12 @@ public class TestMusicCore { mDstoreHandle = Mockito.mock(MusicDataStore.class); preparedQueryObject = Mockito.mock(PreparedQueryObject.class); Mockito.when(mLockHandle.createLockId("/" + "ks1.tn1.pk1")).thenReturn("id1"); - MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); ReturnType expectedResult = new ReturnType(ResultType.FAILURE, "Failure"); - Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(false); - Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) - .thenReturn(musicLockState); ReturnType returnType = MusicCore.atomicPut("ks1", "tn1", "pk1", preparedQueryObject, condition); assertEquals(expectedResult.getResult(), returnType.getResult()); - Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); Mockito.verify(mLockHandle).isMyTurn("id1"); - Mockito.verify(mLockHandle, Mockito.atLeastOnce()) - .getLockState("ks1" + "." + "tn1" + "." + "pk1"); Mockito.verify(mLockHandle).createLockId("/" + "ks1.tn1.pk1"); } @@ -396,19 +389,11 @@ public class TestMusicCore { preparedQueryObject = Mockito.mock(PreparedQueryObject.class); rs = Mockito.mock(ResultSet.class); Mockito.when(mLockHandle.createLockId("/" + "ks1.tn1.pk1")).thenReturn("id1"); - MusicLockState musicLockState = new MusicLockState(LockStatus.LOCKED, "id1"); - Mockito.when(mLockHandle.getLockState("ks1.tn1.pk1")).thenReturn(musicLockState); Mockito.when(mLockHandle.isMyTurn("id1")).thenReturn(false); - Mockito.when(mLockHandle.getLockState("ks1" + "." + "tn1" + "." + "pk1")) - .thenReturn(musicLockState); ResultSet rs1 = MusicCore.atomicGet("ks1", "tn1", "pk1", preparedQueryObject); assertNull(rs1); Mockito.verify(mLockHandle).createLockId("/" + "ks1.tn1.pk1"); - Mockito.verify(mLockHandle, Mockito.atLeastOnce()).getLockState("ks1.tn1.pk1"); Mockito.verify(mLockHandle).isMyTurn("id1"); - Mockito.verify(mLockHandle, Mockito.atLeastOnce()) - .getLockState("ks1" + "." + "tn1" + "." + "pk1"); - } @Test diff --git a/version.properties b/version.properties index 1e4caa5e..cc011b98 100644 --- a/version.properties +++ b/version.properties @@ -4,7 +4,7 @@ major=2 minor=4 -patch=5 +patch=11 base_version=${major}.${minor}.${patch} -- cgit 1.2.3-korg