I'm trying to implement a cache which will count the number of login attempts in the last 5 minutes, in my code i want to check if the user has attempted more than MAX_ATTEMPTS.
In all the code examples i found online for "Guava Cache" use the load method to fetch the value from some other source or calculate it using some method, how can i increment it every time there is a cache hit ?
static LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
.maximumSize(100000)
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(
new CacheLoader<String, Integer>() {
public Integerload(String user) {
return ????;
}
}
);
later at runtime i would like to check:
if(cache.getIfPresent(user) != null && cache.get(user) > MAX_ATTEMPTS)
and also increment it if:
if(cache.getIfPresent(user) != null && cache.get(user) <= MAX_ATTEMPTS)
@Oren Your solution isn't thread-safe, since you're operating on a value outside Cache. You'd better use Cache#asMap()
view and change value inside ConcurrentMap#compute(K, BiFunction<K, V, V>)
method:
forgetPasswordCache.asMap().compute(email, (cachedEmail, currentCount) -> {
if (currentCount != null && currentCount >= RESET_PASSWORD_MAX_ATTEMPTS) {
logger.error("User with id: " + user.getId() + " and email: " + email +
" has reached the maximum number of reset password attempts, the mail will not be sent");
return null;
}
if (currentCount == null) {
return 1;
} else {
return currentCount + 1;
}
});
You can try and adapt the following snippet:
public class Demo {
public static void main(String[] x) {
CacheLoader<String, AtomicInteger> initialValueLoader = new CacheLoader<String, AtomicInteger>() {
@Override
public AtomicInteger load(String key) {
// do not care of the key. everybody starts with 0 login attempts.
return new AtomicInteger(0);
}
};
LoadingCache<String, AtomicInteger> c = CacheBuilder
.newBuilder()
.maximumSize(100000)
.expireAfterAccess(2, TimeUnit.SECONDS)
.build(initialValueLoader);
String user = "bob";
try {
// when the user tries to login, increment the attemps by one
Verify.verify(c.get(user).incrementAndGet() == 1);
// next call will get one
Verify.verify(c.get(user).get() == 1);
} catch (ExecutionException e) {
throw new RuntimeException("oups: " + e, e);
}
// simulate time
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
// after some time, the cache is cleared
try {
Verify.verify(c.get(user).get() == 0);
} catch (ExecutionException e) {
throw new RuntimeException("oups: " + e, e);
}
System.out.println("done.");
}
}
SOLVED by using only the Cache functionality of Guava ( without using the loader )
The cache looks like this:
public class ForgetPasswordAttemptsCache {
private static final Cache<String, Integer> forgetPasswordCache = CacheBuilder.newBuilder()
.expireAfterAccess(24, TimeUnit.HOURS)
.build();
private ForgetPasswordAttemptsCache(){
}
public static Cache<String, Integer> getInstance() {
return forgetPasswordCache;
}
}
and the use of it:
final String email = user.getEmail();
Integer currentCount = ForgetPasswordAttemptsCache.getInstance().getIfPresent(email);
if (currentCount != null && currentCount >= RESET_PASSWORD_MAX_ATTEMPTS) {
logger.error("User with id: " + user.getId() + " and email: " + email +
" has reached the maximum number of reset password attempts, the mail will not be sent");
return;
}
if (currentCount == null) {
ForgetPasswordAttemptsCache.getInstance().put(email, new Integer(1));
} else {
ForgetPasswordAttemptsCache.getInstance().put(email, new Integer(currentCount + 1));
}
Thanks spi for this solution. (now i'm looking for a way to test it without having a test that runs 24 hours.)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.