#949 Create ExpiringSet, integrate into SessionManager

This commit is contained in:
ljacqu
2017-02-19 09:06:15 +01:00
parent 7b3bd3f4ea
commit ca708e23cd
4 changed files with 220 additions and 69 deletions
@@ -5,13 +5,10 @@ import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.util.ExpiringSet;
import javax.inject.Inject;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static fr.xephi.authme.util.Utils.MILLIS_PER_MINUTE;
import java.util.concurrent.TimeUnit;
/**
* Manages sessions, allowing players to be automatically logged in if they join again
@@ -19,15 +16,14 @@ import static fr.xephi.authme.util.Utils.MILLIS_PER_MINUTE;
*/
public class SessionManager implements SettingsDependent, HasCleanup {
// Player -> expiration of session in milliseconds
private final Map<String, Long> sessions = new ConcurrentHashMap<>();
private final ExpiringSet<String> sessions;
private boolean enabled;
private int timeoutInMinutes;
@Inject
SessionManager(Settings settings) {
reload(settings);
long timeout = settings.getProperty(PluginSettings.SESSIONS_TIMEOUT);
sessions = new ExpiringSet<>(timeout, TimeUnit.MINUTES);
enabled = timeout > 0 && settings.getProperty(PluginSettings.SESSIONS_ENABLED);
}
/**
@@ -37,13 +33,7 @@ public class SessionManager implements SettingsDependent, HasCleanup {
* @return True if a session is found.
*/
public boolean hasSession(String name) {
if (enabled) {
Long timeout = sessions.get(name.toLowerCase());
if (timeout != null) {
return System.currentTimeMillis() <= timeout;
}
}
return false;
return enabled && sessions.contains(name.toLowerCase());
}
/**
@@ -53,8 +43,7 @@ public class SessionManager implements SettingsDependent, HasCleanup {
*/
public void addSession(String name) {
if (enabled) {
long timeout = System.currentTimeMillis() + timeoutInMinutes * MILLIS_PER_MINUTE;
sessions.put(name.toLowerCase(), timeout);
sessions.add(name.toLowerCase());
}
}
@@ -64,12 +53,13 @@ public class SessionManager implements SettingsDependent, HasCleanup {
* @param name The name of the player.
*/
public void removeSession(String name) {
this.sessions.remove(name.toLowerCase());
sessions.remove(name.toLowerCase());
}
@Override
public void reload(Settings settings) {
timeoutInMinutes = settings.getProperty(PluginSettings.SESSIONS_TIMEOUT);
long timeoutInMinutes = settings.getProperty(PluginSettings.SESSIONS_TIMEOUT);
sessions.setExpiration(timeoutInMinutes, TimeUnit.MINUTES);
boolean oldEnabled = enabled;
enabled = timeoutInMinutes > 0 && settings.getProperty(PluginSettings.SESSIONS_ENABLED);
@@ -82,16 +72,8 @@ public class SessionManager implements SettingsDependent, HasCleanup {
@Override
public void performCleanup() {
if (!enabled) {
return;
}
final long currentTime = System.currentTimeMillis();
Iterator<Map.Entry<String, Long>> iterator = sessions.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Long> entry = iterator.next();
if (entry.getValue() < currentTime) {
iterator.remove();
}
if (enabled) {
sessions.removeExpiredEntries();
}
}
}
@@ -0,0 +1,97 @@
package fr.xephi.authme.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* Set whose entries expire after a configurable amount of time. Once an entry
* has expired, the set will act as if the entry no longer exists. Time starts
* counting after the entry has been inserted.
* <p>
* Internally, expired entries are not cleared automatically. A cleanup can be
* triggered with {@link #removeExpiredEntries()}. Adding an entry that is
* already present effectively resets its expiration.
*
* @param <E> the type of the entries
*/
public class ExpiringSet<E> {
private Map<E, Long> entries = new ConcurrentHashMap<>();
private long expirationMillis;
/**
* Constructor.
*
* @param duration the duration of time after which entries expire
* @param unit the time unit in which {@code duration} is expressed
*/
public ExpiringSet(long duration, TimeUnit unit) {
setExpiration(duration, unit);
}
/**
* Adds an entry to the set.
*
* @param entry the entry to add
*/
public void add(E entry) {
entries.put(entry, System.currentTimeMillis() + expirationMillis);
}
/**
* Returns whether this set contains the given entry, if it hasn't expired.
*
* @param entry the entry to check
* @return true if the entry is present and not expired, false otherwise
*/
public boolean contains(E entry) {
Long expiration = entries.get(entry);
return expiration != null && expiration > System.currentTimeMillis();
}
/**
* Removes the given entry from the set (if present).
*
* @param entry the entry to remove
*/
public void remove(E entry) {
entries.remove(entry);
}
/**
* Removes all entries from the set.
*/
public void clear() {
entries.clear();
}
/**
* Removes all entries which have expired from the internal structure.
*/
public void removeExpiredEntries() {
entries.entrySet().removeIf(entry -> System.currentTimeMillis() > entry.getValue());
}
/**
* Sets a new expiration duration. Note that already present entries
* will still make use of the old expiration.
*
* @param duration the duration of time after which entries expire
* @param unit the time unit in which {@code duration} is expressed
*/
public void setExpiration(long duration, TimeUnit unit) {
this.expirationMillis = unit.toMillis(duration);
}
/**
* Returns whether this map is empty. This reflects the state of the
* internal map, which may contain expired entries only. The result
* may change after running {@link #removeExpiredEntries()}.
*
* @return true if map is really empty, false otherwise
*/
public boolean isEmpty() {
return entries.isEmpty();
}
}