This is an automated email from the ASF dual-hosted git repository.

samt pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git

commit d0a207b41441823d77b69c62556e6c5d2d4ad88e
Merge: a339aa9 8f33dc0
Author: Sam Tunnicliffe <[email protected]>
AuthorDate: Fri Jul 5 19:06:09 2019 +0100

    Merge branch 'cassandra-3.11' into trunk

 CHANGES.txt                                        |   1 +
 .../apache/cassandra/auth/CassandraAuthorizer.java |  26 ++-
 .../cassandra/auth/CassandraRoleManager.java       |  20 +-
 .../cassandra/auth/PasswordAuthenticator.java      |  42 ++--
 src/java/org/apache/cassandra/auth/Roles.java      |  43 +++-
 .../cassandra/auth/jmx/AuthorizationProxy.java     |   6 -
 .../exceptions/AuthenticationException.java        |   5 +
 .../exceptions/UnauthorizedException.java          |   5 +
 .../org/apache/cassandra/service/ClientState.java  |  16 +-
 .../org/apache/cassandra/auth/AuthCacheTest.java   | 254 ++++++++++++++-------
 10 files changed, 293 insertions(+), 125 deletions(-)

diff --cc src/java/org/apache/cassandra/auth/CassandraAuthorizer.java
index 238b5b5,d4253d1..37ad60a
--- a/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java
+++ b/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java
@@@ -53,7 -63,12 +53,7 @@@ public class CassandraAuthorizer implem
      private static final String RESOURCE = "resource";
      private static final String PERMISSIONS = "permissions";
  
-     SelectStatement authorizeRoleStatement;
 -    // used during upgrades to perform authz on mixed clusters
 -    public static final String USERNAME = "username";
 -    public static final String USER_PERMISSIONS = "permissions";
 -
+     private SelectStatement authorizeRoleStatement;
 -    private SelectStatement legacyAuthorizeRoleStatement;
  
      public CassandraAuthorizer()
      {
@@@ -63,16 -78,23 +63,24 @@@
      // or indirectly via roles granted to the user.
      public Set<Permission> authorize(AuthenticatedUser user, IResource 
resource)
      {
-         if (user.isSuper())
-             return resource.applicablePermissions();
+         try
+         {
+             if (user.isSuper())
+                 return resource.applicablePermissions();
  
-         Set<Permission> permissions = EnumSet.noneOf(Permission.class);
+             Set<Permission> permissions = EnumSet.noneOf(Permission.class);
  
-         // Even though we only care about the RoleResource here, we use 
getRoleDetails as
-         // it saves a Set creation in RolesCache
-         for (Role role: user.getRoleDetails())
-             addPermissionsForRole(permissions, resource, role.resource);
-         return permissions;
 -            for (RoleResource role: user.getRoles())
 -                addPermissionsForRole(permissions, resource, role);
 -
++            // Even though we only care about the RoleResource here, we use 
getRoleDetails as
++            // it saves a Set creation in RolesCache
++            for (Role role: user.getRoleDetails())
++                addPermissionsForRole(permissions, resource, role.resource);
+             return permissions;
+         }
+         catch (RequestExecutionException | RequestValidationException e)
+         {
+             logger.debug("Failed to authorize {} for {}", user, resource);
+             throw new UnauthorizedException("Unable to perform authorization 
of permissions: " + e.getMessage(), e);
+         }
      }
  
      public void grant(AuthenticatedUser performer, Set<Permission> 
permissions, IResource resource, RoleResource grantee)
diff --cc src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index 27a68a0,b1a7227..89f765d
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@@ -29,7 -29,8 +29,8 @@@ import org.slf4j.Logger
  import org.slf4j.LoggerFactory;
  
  import org.apache.cassandra.config.DatabaseDescriptor;
 -import org.apache.cassandra.config.Schema;
 -import org.apache.cassandra.config.SchemaConstants;
++import org.apache.cassandra.exceptions.RequestExecutionException;
 +import org.apache.cassandra.schema.SchemaConstants;
  import org.apache.cassandra.cql3.QueryOptions;
  import org.apache.cassandra.cql3.QueryProcessor;
  import org.apache.cassandra.cql3.UntypedResultSet;
@@@ -98,27 -103,55 +99,34 @@@ public class PasswordAuthenticator impl
          return new AuthenticatedUser(username);
      }
  
 -    private String queryHashedPassword(String username) throws 
AuthenticationException
 +    private String queryHashedPassword(String username)
      {
-         ResultMessage.Rows rows =
-         authenticateStatement.execute(QueryState.forInternalCalls(),
-                                         
QueryOptions.forInternalCalls(consistencyForRole(username),
-                                                                       
Lists.newArrayList(ByteBufferUtil.bytes(username))),
-                                         System.nanoTime());
- 
-         // If either a non-existent role name was supplied, or no credentials
-         // were found for that role we don't want to cache the result so we 
throw
-         // a specific, but unchecked, exception to keep LoadingCache happy.
-         if (rows.result.isEmpty())
-             throw new AuthenticationException(String.format("Provided 
username %s and/or password are incorrect", username));
- 
-         UntypedResultSet result = UntypedResultSet.create(rows.result);
-         if (!result.one().has(SALTED_HASH))
-             throw new AuthenticationException(String.format("Provided 
username %s and/or password are incorrect", username));
- 
-         return result.one().getString(SALTED_HASH);
+         try
+         {
 -            SelectStatement authenticationStatement = 
authenticationStatement();
 -
+             ResultMessage.Rows rows =
 -                authenticationStatement.execute(QueryState.forInternalCalls(),
 -                                                
QueryOptions.forInternalCalls(consistencyForRole(username),
 -                                                                              
Lists.newArrayList(ByteBufferUtil.bytes(username))),
 -                                                System.nanoTime());
++            authenticateStatement.execute(QueryState.forInternalCalls(),
++                                            
QueryOptions.forInternalCalls(consistencyForRole(username),
++                                                                          
Lists.newArrayList(ByteBufferUtil.bytes(username))),
++                                            System.nanoTime());
+ 
+             // If either a non-existent role name was supplied, or no 
credentials
+             // were found for that role we don't want to cache the result so 
we throw
+             // an exception.
+             if (rows.result.isEmpty())
+                 throw new AuthenticationException(String.format("Provided 
username %s and/or password are incorrect", username));
+ 
+             UntypedResultSet result = UntypedResultSet.create(rows.result);
+             if (!result.one().has(SALTED_HASH))
+                 throw new AuthenticationException(String.format("Provided 
username %s and/or password are incorrect", username));
+ 
+             return result.one().getString(SALTED_HASH);
+         }
+         catch (RequestExecutionException e)
+         {
+             throw new AuthenticationException("Unable to perform 
authentication: " + e.getMessage(), e);
+         }
      }
  
 -    /**
 -     * If the legacy users table exists try to verify credentials there. This 
is to handle the case
 -     * where the cluster is being upgraded and so is running with mixed 
versions of the authn tables
 -     */
 -    private SelectStatement authenticationStatement()
 -    {
 -        if (Schema.instance.getCFMetaData(SchemaConstants.AUTH_KEYSPACE_NAME, 
LEGACY_CREDENTIALS_TABLE) == null)
 -            return authenticateStatement;
 -        else
 -        {
 -            // the statement got prepared, we to try preparing it again.
 -            // If the credentials was initialised only after statement got 
prepared, re-prepare (CASSANDRA-12813).
 -            if (legacyAuthenticateStatement == null)
 -                prepareLegacyAuthenticateStatement();
 -            return legacyAuthenticateStatement;
 -        }
 -    }
 -
 -
      public Set<DataResource> protectedResources()
      {
          // Also protected by CassandraRoleManager, but the duplication 
doesn't hurt and is more explicit
diff --cc src/java/org/apache/cassandra/auth/Roles.java
index 22eb0d3,2b1ff6e..527451e
--- a/src/java/org/apache/cassandra/auth/Roles.java
+++ b/src/java/org/apache/cassandra/auth/Roles.java
@@@ -17,46 -17,25 +17,53 @@@
   */
  package org.apache.cassandra.auth;
  
 +import java.util.Collections;
 +import java.util.Map;
  import java.util.Set;
 +import java.util.function.BooleanSupplier;
 +import java.util.stream.Collectors;
 +
 +import com.google.common.annotations.VisibleForTesting;
  
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ 
  import org.apache.cassandra.config.DatabaseDescriptor;
+ import org.apache.cassandra.exceptions.RequestExecutionException;
+ import org.apache.cassandra.exceptions.UnauthorizedException;
  
  public class Roles
  {
+     private static final Logger logger = LoggerFactory.getLogger(Roles.class);
+ 
 -    private static final RolesCache cache = new 
RolesCache(DatabaseDescriptor.getRoleManager());
 +    private static final Role NO_ROLE = new Role("", false, false, 
Collections.emptyMap(), Collections.emptySet());
 +
 +    private static RolesCache cache;
 +    static
 +    {
 +        initRolesCache(DatabaseDescriptor.getRoleManager(),
 +                       () -> 
DatabaseDescriptor.getAuthenticator().requireAuthentication());
 +    }
 +
 +    @VisibleForTesting
 +    public static void initRolesCache(IRoleManager roleManager, 
BooleanSupplier enableCache)
 +    {
 +        if (cache != null)
 +            cache.unregisterMBean();
 +        cache = new RolesCache(roleManager, enableCache);
 +    }
 +
 +    @VisibleForTesting
 +    public static void clearCache()
 +    {
 +        cache.invalidate();
 +    }
  
      /**
 -     * Get all roles granted to the supplied Role, including both directly 
granted
 +     * Identify all roles granted to the supplied Role, including both 
directly granted
       * and inherited roles.
 -     * The returned roles may be cached if {@code roles_validity_in_ms > 0}
 +     * This method is used where we mainly just care about *which* roles are 
granted to a given role,
 +     * including when looking up or listing permissions for a role on a given 
resource.
       *
       * @param primaryRole the Role
       * @return set of all granted Roles for the primary Role
@@@ -91,86 -54,18 +98,102 @@@
       */
      public static boolean hasSuperuserStatus(RoleResource role)
      {
-         for (Role r : getRoleDetails(role))
-             if (r.isSuper)
-                 return true;
+         try
+         {
 -            IRoleManager roleManager = DatabaseDescriptor.getRoleManager();
 -            for (RoleResource r : cache.getRoles(role))
 -                if (roleManager.isSuper(r))
++            for (Role r : getRoleDetails(role))
++                if (r.isSuper)
+                     return true;
 +
-         return false;
+             return false;
+         }
+         catch (RequestExecutionException e)
+         {
+             logger.debug("Failed to authorize {} for super-user permission", 
role.getRoleName());
+             throw new UnauthorizedException("Unable to perform authorization 
of super-user permission: " + e.getMessage(), e);
+         }
      }
 +
 +    /**
 +     * Returns true if the supplied role has the login privilege. This cannot 
be inherited, so
 +     * returns true iff the named role has that bit set.
 +     * @param role the role identifier
 +     * @return true if the role has the canLogin privilege, false otherwise
 +     */
 +    public static boolean canLogin(final RoleResource role)
 +    {
-         for (Role r : getRoleDetails(role))
-             if (r.resource.equals(role))
-                 return r.canLogin;
++        try
++        {
++            for (Role r : getRoleDetails(role))
++                if (r.resource.equals(role))
++                    return r.canLogin;
 +
-         return false;
++            return false;
++        }
++        catch (RequestExecutionException e)
++        {
++            logger.debug("Failed to authorize {} for login permission", 
role.getRoleName());
++            throw new UnauthorizedException("Unable to perform authorization 
of login permission: " + e.getMessage(), e);
++        }
 +    }
 +
 +    /**
 +     * Returns the map of custom options for the named role. These options 
are not inherited from granted roles, but
 +     * are set directly.
 +     * @param role the role identifier
 +     * @return map of option_name -> value. If no options are set for the 
named role, the map will be empty
 +     * but never null.
 +     */
 +    public static Map<String, String> getOptions(RoleResource role)
 +    {
 +        for (Role r : getRoleDetails(role))
 +            if (r.resource.equals(role))
 +                return r.options;
 +
 +        return NO_ROLE.options;
 +    }
 +
 +   /**
 +    * Return the NullObject Role instance which can be safely used to 
indicate no information is available
 +    * when querying for a specific named role.
 +    * @return singleton null role object
 +    */
 +   public static Role nullRole()
 +   {
 +       return NO_ROLE;
 +   }
 +
 +   /**
 +    * Just a convenience method which compares a role instance with the null 
object version, indicating if the
 +    * return from some query/lookup method was a valid Role or indicates that 
the role does not exist.
 +    * @param role
 +    * @return true if the supplied role is the null role instance, false 
otherwise.
 +    */
 +   public static boolean isNullRole(Role role)
 +   {
 +       return NO_ROLE.equals(role);
 +   }
 +
 +
 +   /**
 +    * Constructs a Role object from a RoleResource, using the methods of the 
supplied IRoleManager.
 +    * This is used by the default implementation of 
IRoleManager#getRoleDetails so that IRoleManager impls
 +    * which don't implement an optimized getRoleDetails remain compatible. 
Depending on the IRoleManager
 +    * implementation this could be quite heavyweight, so should not be used 
on any hot path.
 +    *
 +    * @param resource identifies the role
 +    * @param roleManager provides lookup functions to retrieve role info
 +    * @return Role object including superuser status, login privilege, custom 
options and the set of roles
 +    * granted to identified role.
 +    */
 +   public static Role fromRoleResource(RoleResource resource, IRoleManager 
roleManager)
 +   {
 +       return new Role(resource.getName(),
 +                       roleManager.isSuper(resource),
 +                       roleManager.canLogin(resource),
 +                       roleManager.getCustomOptions(resource),
 +                       roleManager.getRoles(resource, false)
 +                                  .stream()
 +                                  .map(RoleResource::getRoleName)
 +                                  .collect(Collectors.toSet()));
 +   }
  }
diff --cc src/java/org/apache/cassandra/auth/jmx/AuthorizationProxy.java
index ef00027,7bfbf52..b213c43
--- a/src/java/org/apache/cassandra/auth/jmx/AuthorizationProxy.java
+++ b/src/java/org/apache/cassandra/auth/jmx/AuthorizationProxy.java
@@@ -33,7 -32,7 +33,6 @@@ import javax.management.ObjectName
  import javax.security.auth.Subject;
  
  import com.google.common.annotations.VisibleForTesting;
--import com.google.common.base.Throwables;
  import com.google.common.collect.ImmutableSet;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
diff --cc src/java/org/apache/cassandra/service/ClientState.java
index 26ed271,15262d7..81574e6
--- a/src/java/org/apache/cassandra/service/ClientState.java
+++ b/src/java/org/apache/cassandra/service/ClientState.java
@@@ -30,12 -28,10 +30,14 @@@ import org.slf4j.Logger
  import org.slf4j.LoggerFactory;
  
  import org.apache.cassandra.auth.*;
 -import org.apache.cassandra.config.CFMetaData;
 +import org.apache.cassandra.db.virtual.VirtualSchemaKeyspace;
++import org.apache.cassandra.exceptions.RequestExecutionException;
++import org.apache.cassandra.exceptions.RequestValidationException;
 +import org.apache.cassandra.schema.TableMetadata;
 +import org.apache.cassandra.schema.TableMetadataRef;
  import org.apache.cassandra.config.DatabaseDescriptor;
 -import org.apache.cassandra.config.Schema;
 -import org.apache.cassandra.config.SchemaConstants;
 +import org.apache.cassandra.schema.Schema;
 +import org.apache.cassandra.schema.SchemaConstants;
  import org.apache.cassandra.cql3.QueryHandler;
  import org.apache.cassandra.cql3.QueryProcessor;
  import org.apache.cassandra.cql3.functions.Function;
@@@ -321,15 -264,38 +323,27 @@@ public class ClientStat
      /**
       * Attempts to login the given user.
       */
 -    public void login(AuthenticatedUser user) throws AuthenticationException
 +    public void login(AuthenticatedUser user)
      {
-         if (user.isAnonymous() || user.canLogin())
 -        // Login privilege is not inherited via granted roles, so just
 -        // verify that the role with the credentials that were actually
 -        // supplied has it
+         if (user.isAnonymous() || canLogin(user))
              this.user = user;
          else
              throw new AuthenticationException(String.format("%s is not 
permitted to log in", user.getName()));
      }
  
+     private boolean canLogin(AuthenticatedUser user)
+     {
+         try
+         {
 -            return 
DatabaseDescriptor.getRoleManager().canLogin(user.getPrimaryRole());
 -        } catch (RequestExecutionException e) {
++            return user.canLogin();
++        }
++        catch (RequestExecutionException | RequestValidationException e)
++        {
+             throw new AuthenticationException("Unable to perform 
authentication: " + e.getMessage(), e);
+         }
+     }
+ 
 -    public void hasAllKeyspacesAccess(Permission perm) throws 
UnauthorizedException
 +    public void ensureAllKeyspacesPermission(Permission perm)
      {
          if (isInternal)
              return;
diff --cc test/unit/org/apache/cassandra/auth/AuthCacheTest.java
index cc78ebc,0030603..217821e
--- a/test/unit/org/apache/cassandra/auth/AuthCacheTest.java
+++ b/test/unit/org/apache/cassandra/auth/AuthCacheTest.java
@@@ -18,16 -17,16 +17,20 @@@
   */
  package org.apache.cassandra.auth;
  
- import org.junit.Assert;
- import org.junit.BeforeClass;
 -import java.util.function.Consumer;
++import java.util.function.BooleanSupplier;
+ import java.util.function.Function;
 -import java.util.function.Supplier;
++import java.util.function.IntConsumer;
++import java.util.function.IntSupplier;
+ 
  import org.junit.Test;
  
- import org.apache.cassandra.config.DatabaseDescriptor;
+ import org.apache.cassandra.db.ConsistencyLevel;
+ import org.apache.cassandra.exceptions.UnavailableException;
  
- import static org.junit.Assert.assertFalse;
+ import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertNull;
 +import static org.junit.Assert.assertTrue;
  
  public class AuthCacheTest
  {
@@@ -41,97 -46,119 +50,182 @@@
      }
  
      @Test
-     public void testCaching()
-     {
-         AuthCache<String, String> authCache = new AuthCache<>("TestCache",
-                                                               
DatabaseDescriptor::setCredentialsValidity,
-                                                               
DatabaseDescriptor::getCredentialsValidity,
-                                                               
DatabaseDescriptor::setCredentialsUpdateInterval,
-                                                               
DatabaseDescriptor::getCredentialsUpdateInterval,
-                                                               
DatabaseDescriptor::setCredentialsCacheMaxEntries,
-                                                               
DatabaseDescriptor::getCredentialsCacheMaxEntries,
-                                                               this::load,
-                                                               () -> true
-         );
- 
-         // Test cacheloader is called if set
-         loadFuncCalled = false;
-         String result = authCache.get("test");
-         assertTrue(loadFuncCalled);
-         Assert.assertEquals("load", result);
- 
-         // value should be fetched from cache
-         loadFuncCalled = false;
-         String result2 = authCache.get("test");
-         assertFalse(loadFuncCalled);
-         Assert.assertEquals("load", result2);
- 
-         // value should be fetched from cache after complete invalidate
+     public void testCacheLoaderIsNotCalledOnSecond()
+     {
+         TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
+         authCache.get("10");
+         assertEquals(1, loadCounter);
+ 
+         int result = authCache.get("10");
+ 
+         assertEquals(10, result);
+         assertEquals(1, loadCounter);
+     }
+ 
+     @Test
+     public void testCacheLoaderIsAlwaysCalledWhenDisabled()
+     {
+         isCacheEnabled = false;
+         TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
+ 
+         authCache.get("10");
+         int result = authCache.get("10");
+ 
+         assertEquals(10, result);
+         assertEquals(2, loadCounter);
+     }
+ 
+     @Test
+     public void testCacheLoaderIsAlwaysCalledWhenValidityIsZero()
+     {
+         setValidity(0);
+         TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
+ 
+         authCache.get("10");
+         int result = authCache.get("10");
+ 
+         assertEquals(10, result);
+         assertEquals(2, loadCounter);
+     }
+ 
+     @Test
+     public void testCacheLoaderIsCalledAfterFullInvalidate()
+     {
+         TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
+         authCache.get("10");
+ 
          authCache.invalidate();
-         loadFuncCalled = false;
-         String result3 = authCache.get("test");
-         assertTrue(loadFuncCalled);
-         Assert.assertEquals("load", result3);
- 
-         // value should be fetched from cache after invalidating key
-         authCache.invalidate("test");
-         loadFuncCalled = false;
-         String result4 = authCache.get("test");
-         assertTrue(loadFuncCalled);
-         Assert.assertEquals("load", result4);
- 
-         // set cache to null and load function should be called
-         loadFuncCalled = false;
+         int result = authCache.get("10");
+ 
+         assertEquals(10, result);
+         assertEquals(2, loadCounter);
+     }
+ 
+     @Test
+     public void testCacheLoaderIsCalledAfterInvalidateKey()
+     {
+         TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
+         authCache.get("10");
+ 
+         authCache.invalidate("10");
+         int result = authCache.get("10");
+ 
+         assertEquals(10, result);
+         assertEquals(2, loadCounter);
+     }
+ 
++    @Test
++    public void testCacheLoaderIsCalledAfterReset()
++    {
++        TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
++        authCache.get("10");
++
 +        authCache.cache = null;
-         String result5 = authCache.get("test");
-         assertTrue(loadFuncCalled);
-         Assert.assertEquals("load", result5);
++        int result = authCache.get("10");
++
++        assertEquals(10, result);
++        assertEquals(2, loadCounter);
 +    }
 +
 +    @Test
-     public void testInitCache()
-     {
-         // Test that a validity of <= 0 will turn off caching
-         DatabaseDescriptor.setCredentialsValidity(0);
-         AuthCache<String, String> authCache = new AuthCache<>("TestCache2",
-                                                               
DatabaseDescriptor::setCredentialsValidity,
-                                                               
DatabaseDescriptor::getCredentialsValidity,
-                                                               
DatabaseDescriptor::setCredentialsUpdateInterval,
-                                                               
DatabaseDescriptor::getCredentialsUpdateInterval,
-                                                               
DatabaseDescriptor::setCredentialsCacheMaxEntries,
-                                                               
DatabaseDescriptor::getCredentialsCacheMaxEntries,
-                                                               this::load,
-                                                               () -> true);
++    public void testThatZeroValidityTurnOffCaching()
++    {
++        setValidity(0);
++        TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
++        authCache.get("10");
++        int result = authCache.get("10");
++
 +        assertNull(authCache.cache);
++        assertEquals(10, result);
++        assertEquals(2, loadCounter);
++    }
++
++    @Test
++    public void testThatRaisingValidityTurnOnCaching()
++    {
++        setValidity(0);
++        TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
++
 +        authCache.setValidity(2000);
 +        authCache.cache = authCache.initCache(null);
++
 +        assertNotNull(authCache.cache);
++    }
++
++    @Test
++    public void testDisableCache()
++    {
++        isCacheEnabled = false;
++        TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
 +
-         // Test enableCache works as intended
-         authCache = new AuthCache<>("TestCache3",
-                                     
DatabaseDescriptor::setCredentialsValidity,
-                                     
DatabaseDescriptor::getCredentialsValidity,
-                                     
DatabaseDescriptor::setCredentialsUpdateInterval,
-                                     
DatabaseDescriptor::getCredentialsUpdateInterval,
-                                     
DatabaseDescriptor::setCredentialsCacheMaxEntries,
-                                     
DatabaseDescriptor::getCredentialsCacheMaxEntries,
-                                     this::load,
-                                     () -> isCacheEnabled);
 +        assertNull(authCache.cache);
++    }
++
++    @Test
++    public void testDynamicallyEnableCache()
++    {
++        isCacheEnabled = false;
++        TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
++
 +        isCacheEnabled = true;
 +        authCache.cache = authCache.initCache(null);
++
 +        assertNotNull(authCache.cache);
++    }
++
++    @Test
++    public void testDefaultPolicies()
++    {
++        TestCache<String, Integer> authCache = new 
TestCache<>(this::countingLoader, this::setValidity, () -> validity, () -> 
isCacheEnabled);
 +
-         // Ensure at a minimum these policies have been initialised by default
 +        assertTrue(authCache.cache.policy().expireAfterWrite().isPresent());
 +        assertTrue(authCache.cache.policy().refreshAfterWrite().isPresent());
 +        assertTrue(authCache.cache.policy().eviction().isPresent());
 +    }
 +
-     private String load(String test)
+     @Test(expected = UnavailableException.class)
+     public void testCassandraExceptionPassThroughWhenCacheEnabled()
+     {
 -        TestCache<String, Integer> cache = new TestCache<>(s -> {
 -            throw new UnavailableException(ConsistencyLevel.QUORUM, 3, 1);
 -        }, this::setValidity, () -> validity, () -> isCacheEnabled);
++        TestCache<String, Integer> cache = new TestCache<>(s -> { throw 
UnavailableException.create(ConsistencyLevel.QUORUM, 3, 1); }, 
this::setValidity, () -> validity, () -> isCacheEnabled);
+ 
+         cache.get("expect-exception");
+     }
+ 
+     @Test(expected = UnavailableException.class)
+     public void testCassandraExceptionPassThroughWhenCacheDisable()
      {
-         loadFuncCalled = true;
-         return "load";
+         isCacheEnabled = false;
 -        TestCache<String, Integer> cache = new TestCache<>(s -> {
 -            throw new UnavailableException(ConsistencyLevel.QUORUM, 3, 1);
 -        }, this::setValidity, () -> validity, () -> isCacheEnabled);
++        TestCache<String, Integer> cache = new TestCache<>(s -> { throw 
UnavailableException.create(ConsistencyLevel.QUORUM, 3, 1); }, 
this::setValidity, () -> validity, () -> isCacheEnabled);
+ 
+         cache.get("expect-exception");
      }
  
+     private void setValidity(int validity)
+     {
+         this.validity = validity;
+     }
+ 
+     private Integer countingLoader(String s)
+     {
+         loadCounter++;
+         return Integer.parseInt(s);
+     }
+ 
+     private static class TestCache<K, V> extends AuthCache<K, V>
+     {
+         private static int nameCounter = 0; // Allow us to create many 
instances of cache with same name prefix
+ 
 -        TestCache(Function<K, V> loadFunction, Consumer<Integer> 
setValidityDelegate, Supplier<Integer> getValidityDelegate, Supplier<Boolean> 
cacheEnabledDelegate)
++        TestCache(Function<K, V> loadFunction, IntConsumer 
setValidityDelegate, IntSupplier getValidityDelegate, BooleanSupplier 
cacheEnabledDelegate)
+         {
+             super("TestCache" + nameCounter++,
+                   setValidityDelegate,
+                   getValidityDelegate,
 -                  (updateInterval) -> {
 -                  },
++                  (updateInterval) -> {},
+                   () -> 1000,
 -                  (maxEntries) -> {
 -                  },
++                  (maxEntries) -> {},
+                   () -> 10,
+                   loadFunction,
+                   cacheEnabledDelegate);
+         }
+     }
  }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to