forked from TuxCoding/FastLogin
Refactor a lot of code + Add Guava v10 as shared library
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.base.Ticker;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
|
||||
/**
|
||||
* Represents a Guava CacheBuilder that is compatible with both Guava 10 (Minecraft 1.7.X) and 13
|
||||
*/
|
||||
public class CompatibleCacheBuilder<K, V> {
|
||||
|
||||
private static Method BUILD_METHOD;
|
||||
private static Method AS_MAP_METHOD;
|
||||
|
||||
/**
|
||||
* Construct a new safe cache builder.
|
||||
*
|
||||
* @param <K> Key type
|
||||
* @param <V> Value type
|
||||
*
|
||||
* @return A new cache builder.
|
||||
*/
|
||||
public static <K, V> CompatibleCacheBuilder<K, V> newBuilder() {
|
||||
return new CompatibleCacheBuilder<K, V>();
|
||||
}
|
||||
|
||||
private CacheBuilder<K, V> builder;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private CompatibleCacheBuilder() {
|
||||
builder = (CacheBuilder<K, V>) CacheBuilder.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The table is
|
||||
* internally partitioned to try to permit the indicated number of concurrent updates without contention. Because
|
||||
* assignment of entries to these partitions is not necessarily uniform, the actual concurrency observed may vary.
|
||||
* Ideally, you should choose a value to accommodate as many threads as will ever concurrently modify the table.
|
||||
* Using a significantly higher value than you need can waste space and time, and a significantly lower value can
|
||||
* lead to thread contention. But overestimates and underestimates within an order of magnitude do not usually have
|
||||
* much noticeable impact. A value of one permits only one thread to modify the cache at a time, but since read
|
||||
* operations can proceed concurrently, this still yields higher concurrency than full synchronization. Defaults to
|
||||
* 4.
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b>The default may change in the future. If you care about this value, you should always choose it
|
||||
* explicitly.
|
||||
*
|
||||
* @param concurrencyLevel New concurrency level
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive
|
||||
* @throws IllegalStateException if a concurrency level was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> concurrencyLevel(int concurrencyLevel) {
|
||||
builder.concurrencyLevel(concurrencyLevel);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after
|
||||
* the entry's creation, or last access. Access time is reset by
|
||||
* {@link com.google.common.cache.Cache#get Cache.get()}, but not by operations on the view returned by
|
||||
* {@link com.google.common.cache.Cache#asMap() Cache.asMap()}.
|
||||
*
|
||||
* <p>
|
||||
* When {@code duration} is zero, elements will be evicted immediately after being loaded into the cache. This has
|
||||
* the same effect as invoking {@link #maximumSize maximumSize}{@code (0)}. It can be useful in testing, or to
|
||||
* disable caching temporarily without a code change.
|
||||
*
|
||||
* <p>
|
||||
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
|
||||
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
|
||||
* occasional read operations in the absense of writes; though this behavior may change in the future.
|
||||
*
|
||||
* @param duration the length of time after an entry is last accessed that it should be automatically removed
|
||||
* @param unit the unit that {@code duration} is expressed in
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code duration} is negative
|
||||
* @throws IllegalStateException if the time to idle or time to live was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> expireAfterAccess(long duration, TimeUnit unit) {
|
||||
builder.expireAfterAccess(duration, unit);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after
|
||||
* the entry's creation, or the most recent replacement of its value.
|
||||
*
|
||||
* <p>
|
||||
* When {@code duration} is zero, elements will be evicted immediately after being loaded into the cache. This has
|
||||
* the same effect as invoking {@link #maximumSize maximumSize}{@code (0)}. It can be useful in testing, or to
|
||||
* disable caching temporarily without a code change.
|
||||
*
|
||||
* <p>
|
||||
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
|
||||
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
|
||||
* occasional read operations in the absense of writes; though this behavior may change in the future.
|
||||
*
|
||||
* @param duration the length of time after an entry is created that it should be automatically removed
|
||||
* @param unit the unit that {@code duration} is expressed in
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code duration} is negative
|
||||
* @throws IllegalStateException if the time to live or time to idle was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
|
||||
builder.expireAfterWrite(duration, unit);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum total size for the internal hash tables. For example, if the initial capacity is {@code 60}, and
|
||||
* the concurrency level is {@code 8}, then eight segments are created, each having a hash table of size eight.
|
||||
* Providing a large enough estimate at construction time avoids the need for expensive resizing operations later,
|
||||
* but setting this value unnecessarily high wastes memory.
|
||||
*
|
||||
* @param initialCapacity - initial capacity
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code initialCapacity} is negative
|
||||
* @throws IllegalStateException if an initial capacity was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> initialCapacity(int initialCapacity) {
|
||||
builder.initialCapacity(initialCapacity);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the maximum number of entries the cache may contain. Note that the cache <b>may evict an entry before
|
||||
* this limit is exceeded</b>. As the cache size grows close to the maximum, the cache evicts entries that are less
|
||||
* likely to be used again. For example, the cache may evict an entry because it hasn't been used recently or very
|
||||
* often.
|
||||
*
|
||||
* <p>
|
||||
* When {@code size} is zero, elements will be evicted immediately after being loaded into the cache. This has the
|
||||
* same effect as invoking {@link #expireAfterWrite expireAfterWrite}{@code (0, unit)} or {@link #expireAfterAccess expireAfterAccess}{@code (0,
|
||||
* unit)}. It can be useful in testing, or to disable caching temporarily without a code change.
|
||||
*
|
||||
* @param size the maximum size of the cache
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code size} is negative
|
||||
* @throws IllegalStateException if a maximum size was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> maximumSize(int size) {
|
||||
builder.maximumSize(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a listener instance, which all caches built using this {@code CacheBuilder} will notify each time an
|
||||
* entry is removed from the cache by any means.
|
||||
*
|
||||
* <p>
|
||||
* Each cache built by this {@code CacheBuilder} after this method is called invokes the supplied listener after
|
||||
* removing an element for any reason (see removal causes in
|
||||
* {@link com.google.common.cache.RemovalCause RemovalCause}). It will invoke the listener during invocations of any
|
||||
* of that cache's public methods (even read-only methods).
|
||||
*
|
||||
* <p>
|
||||
* <b>Important note:</b> Instead of returning <em>this</em> as a {@code CacheBuilder} instance, this method returns
|
||||
* {@code CacheBuilder<K1, V1>}. From this point on, either the original reference or the returned reference may be
|
||||
* used to complete configuration and build the cache, but only the "generic" one is type-safe. That is, it will
|
||||
* properly prevent you from building caches whose key or value types are incompatible with the types accepted by
|
||||
* the listener already provided; the {@code CacheBuilder} type cannot do this. For best results, simply use the
|
||||
* standard method-chaining idiom, as illustrated in the documentation at top, configuring a {@code CacheBuilder}
|
||||
* and building your {@link com.google.common.cache.Cache Cache} all in a single statement.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> if you ignore the above advice, and use this {@code CacheBuilder} to build a cache whose key or
|
||||
* value type is incompatible with the listener, you will likely experience a {@link ClassCastException} at some
|
||||
* <i>undefined</i> point in the future.
|
||||
*
|
||||
* @param <K1> Key type
|
||||
* @param <V1> Value type
|
||||
* @param listener - removal listener
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalStateException if a removal listener was already set
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K1 extends K, V1 extends V> CompatibleCacheBuilder<K1, V1> removalListener(RemovalListener<? super K1, ? super V1> listener) {
|
||||
builder.removalListener(listener);
|
||||
return (CompatibleCacheBuilder<K1, V1>) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a nanosecond-precision time source for use in determining when entries should be expired. By default,
|
||||
* {@link System#nanoTime} is used.
|
||||
*
|
||||
* <p>
|
||||
* The primary intent of this method is to facilitate testing of caches which have been configured with
|
||||
* {@link #expireAfterWrite} or {@link #expireAfterAccess}.
|
||||
*
|
||||
* @param ticker - ticker
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalStateException if a ticker was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> ticker(Ticker ticker) {
|
||||
builder.ticker(ticker);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that each value (not key) stored in the cache should be wrapped in a
|
||||
* {@link java.lang.ref.SoftReference SoftReference} (by default, strong references are used). Softly-referenced
|
||||
* objects will be garbage-collected in a <i>globally</i>
|
||||
* least-recently-used manner, in response to memory demand.
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> in most circumstances it is better to set a per-cache {@linkplain #maximumSize maximum size}
|
||||
* instead of using soft references. You should only use this method if you are well familiar with the practical
|
||||
* consequences of soft references.
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b> when this method is used, the resulting cache will use identity ({@code ==}) comparison to determine
|
||||
* equality of values.
|
||||
*
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalStateException if the value strength was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> softValues() {
|
||||
builder.softValues();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that each key (not value) stored in the cache should be wrapped in a
|
||||
* {@link java.lang.ref.WeakReference WeakReference} (by default, strong references are used).
|
||||
*
|
||||
* <p>
|
||||
* <b>Warning:</b> when this method is used, the resulting cache will use identity ({@code ==}) comparison to
|
||||
* determine equality of keys.
|
||||
*
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalStateException if the key strength was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> weakKeys() {
|
||||
builder.weakKeys();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that each value (not key) stored in the cache should be wrapped in a
|
||||
* {@link java.lang.ref.WeakReference WeakReference} (by default, strong references are used).
|
||||
*
|
||||
* <p>
|
||||
* Weak values will be garbage collected once they are weakly reachable. This makes them a poor candidate for
|
||||
* caching; consider {@link #softValues} instead.
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b> when this method is used, the resulting cache will use identity ({@code ==}) comparison to determine
|
||||
* equality of values.
|
||||
*
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalStateException if the value strength was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> weakValues() {
|
||||
builder.weakValues();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache wrapped as a ConcurrentMap.
|
||||
* <p>
|
||||
* We can't return the direct Cache instance as it changed in Guava 13.
|
||||
*
|
||||
* @param <K1> Key type
|
||||
* @param <V1> Value type
|
||||
* @param loader - cache loader
|
||||
* @return The cache as a a map.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K1 extends K, V1 extends V> ConcurrentMap<K1, V1> build(CacheLoader<? super K1, V1> loader) {
|
||||
Object cache = null;
|
||||
|
||||
if (BUILD_METHOD == null) {
|
||||
try {
|
||||
BUILD_METHOD = builder.getClass().getDeclaredMethod("build", CacheLoader.class);
|
||||
BUILD_METHOD.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to find CacheBuilder.build(CacheLoader)", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to build the Cache
|
||||
try {
|
||||
cache = BUILD_METHOD.invoke(builder, loader);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to invoke " + BUILD_METHOD + " on " + builder, e);
|
||||
}
|
||||
|
||||
if (AS_MAP_METHOD == null) {
|
||||
try {
|
||||
AS_MAP_METHOD = cache.getClass().getMethod("asMap");
|
||||
AS_MAP_METHOD.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to find Cache.asMap() in " + cache, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve it as a map
|
||||
try {
|
||||
return (ConcurrentMap<K1, V1>) AS_MAP_METHOD.invoke(cache);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to invoke " + AS_MAP_METHOD + " on " + cache, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
/**
|
||||
* Represents a supporting authentication plugin in BungeeCord and Bukkit/Spigot/... servers
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
public interface PasswordGenerator<T> {
|
||||
|
||||
+56
-6
@@ -1,23 +1,51 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.CompatibleCacheBuilder;
|
||||
import com.github.games647.fastlogin.core.SharedConfig;
|
||||
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
|
||||
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
|
||||
import com.github.games647.fastlogin.core.importer.AutoInImporter;
|
||||
import com.github.games647.fastlogin.core.importer.ImportPlugin;
|
||||
import com.github.games647.fastlogin.core.importer.Importer;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class FastLoginCore {
|
||||
|
||||
public abstract class FastLoginCore<T> {
|
||||
|
||||
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
|
||||
CompatibleCacheBuilder<Object, Object> builder = CompatibleCacheBuilder.newBuilder();
|
||||
|
||||
if (expireAfterWrite > 0) {
|
||||
builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
if (maxSize > 0) {
|
||||
builder.maximumSize(maxSize);
|
||||
}
|
||||
|
||||
return builder.build(new CacheLoader<K, V>() {
|
||||
@Override
|
||||
public V load(K key) throws Exception {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static UUID parseId(String withoutDashes) {
|
||||
if (withoutDashes == null) {
|
||||
return null;
|
||||
@@ -33,13 +61,15 @@ public abstract class FastLoginCore {
|
||||
protected final Map<String, String> localeMessages = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentMap<String, Object> pendingLogins;
|
||||
private final Set<UUID> pendingConfirms = Sets.newHashSet();
|
||||
private final SharedConfig sharedConfig;
|
||||
|
||||
private MojangApiConnector mojangApiConnector;
|
||||
private PasswordGenerator<T> passwordGenerator = new DefaultPasswordGenerator<>();
|
||||
private AuthStorage storage;
|
||||
|
||||
public FastLoginCore(ConcurrentMap<String, Object> pendingLogins, Map<String, Object> config) {
|
||||
this.pendingLogins = pendingLogins;
|
||||
public FastLoginCore(Map<String, Object> config) {
|
||||
this.pendingLogins = FastLoginCore.buildCache(5, 0);
|
||||
this.sharedConfig = new SharedConfig(config);
|
||||
}
|
||||
|
||||
@@ -69,7 +99,15 @@ public abstract class FastLoginCore {
|
||||
|
||||
public abstract void loadConfig();
|
||||
|
||||
public boolean setupDatabase(String driver, String host, int port, String database, String user, String password) {
|
||||
public boolean setupDatabase() {
|
||||
String driver = sharedConfig.get("driver");
|
||||
String host = sharedConfig.get("host", "");
|
||||
int port = sharedConfig.get("port", 3306);
|
||||
String database = sharedConfig.get("database");
|
||||
|
||||
String user = sharedConfig.get("username", "");
|
||||
String password = sharedConfig.get("password", "");
|
||||
|
||||
storage = new AuthStorage(this, driver, host, port, database, user, password);
|
||||
try {
|
||||
storage.createTables();
|
||||
@@ -124,10 +162,22 @@ public abstract class FastLoginCore {
|
||||
return sharedConfig;
|
||||
}
|
||||
|
||||
public PasswordGenerator<T> getPasswordGenerator() {
|
||||
return passwordGenerator;
|
||||
}
|
||||
|
||||
public void setPasswordGenerator(PasswordGenerator<T> passwordGenerator) {
|
||||
this.passwordGenerator = passwordGenerator;
|
||||
}
|
||||
|
||||
public ConcurrentMap<String, Object> getPendingLogins() {
|
||||
return pendingLogins;
|
||||
}
|
||||
|
||||
public Set<UUID> getPendingConfirms() {
|
||||
return pendingConfirms;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (storage != null) {
|
||||
storage.close();
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
import com.github.games647.fastlogin.core.SharedConfig;
|
||||
import java.util.UUID;
|
||||
|
||||
+4
-5
@@ -1,5 +1,6 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.BalancedSSLFactory;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -36,17 +37,15 @@ public abstract class MojangApiConnector {
|
||||
//compile the pattern only on plugin enable -> and this have to be threadsafe
|
||||
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
|
||||
|
||||
private final ConcurrentMap<Object, Object> requests;
|
||||
private final ConcurrentMap<Object, Object> requests = FastLoginCore.buildCache(10, -1);
|
||||
private final BalancedSSLFactory sslFactory;
|
||||
private final int rateLimit;
|
||||
private long lastRateLimit;
|
||||
|
||||
protected final Logger logger;
|
||||
|
||||
public MojangApiConnector(ConcurrentMap<Object, Object> requests, Logger logger, List<String> localAddresses
|
||||
, int rateLimit) {
|
||||
public MojangApiConnector(Logger logger, List<String> localAddresses, int rateLimit) {
|
||||
this.logger = logger;
|
||||
this.requests = requests;
|
||||
|
||||
if (rateLimit > 600) {
|
||||
this.rateLimit = 600;
|
||||
Reference in New Issue
Block a user