mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-24 11:38:40 +01:00
Replace huge forwarding classes with java Proxy instances
This commit is contained in:
parent
da797f154d
commit
b8da286f64
@ -57,10 +57,13 @@ import java.util.concurrent.CompletionException;
|
||||
*/
|
||||
public class AbstractStorage implements Storage {
|
||||
public static Storage create(LuckPermsPlugin plugin, AbstractDao backing) {
|
||||
// make a base implementation
|
||||
Storage base = new AbstractStorage(plugin, backing);
|
||||
Storage phased = PhasedStorage.wrap(base);
|
||||
// wrap with a phaser
|
||||
PhasedStorage phased = PhasedStorage.wrap(base);
|
||||
// wrap with a buffer
|
||||
BufferedOutputStorage buffered = BufferedOutputStorage.wrap(phased, 250L);
|
||||
plugin.getBootstrap().getScheduler().asyncRepeating(buffered, 2L);
|
||||
plugin.getBootstrap().getScheduler().asyncRepeating(buffered.buffer(), 2L);
|
||||
return buffered;
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
||||
if (this.connection == null || this.connection.isClosed()) {
|
||||
Connection connection = this.driver.connect("jdbc:h2:" + this.file.getAbsolutePath(), new Properties());
|
||||
if (connection != null) {
|
||||
this.connection = new NonClosableConnection(connection);
|
||||
this.connection = NonClosableConnection.wrap(connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,305 +25,45 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.sql.connection.file;
|
||||
|
||||
import java.sql.Array;
|
||||
import java.sql.Blob;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Clob;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.NClob;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.SQLXML;
|
||||
import java.sql.Savepoint;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Struct;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public final class NonClosableConnection implements Connection {
|
||||
private final Connection delegate;
|
||||
/**
|
||||
* Represents a connection which cannot be closed using the standard {@link #close()} method.
|
||||
*/
|
||||
public interface NonClosableConnection extends Connection {
|
||||
|
||||
public NonClosableConnection(Connection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public void shutdown() throws SQLException {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
// delegate
|
||||
|
||||
@Override
|
||||
public String getCatalog() throws SQLException {
|
||||
return this.delegate.getCatalog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
this.delegate.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return this.delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nativeSQL(String sql) throws SQLException {
|
||||
return this.delegate.nativeSQL(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, autoGeneratedKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob createBlob() throws SQLException {
|
||||
return this.delegate.createBlob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
|
||||
return this.delegate.createStruct(typeName, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransactionIsolation() throws SQLException {
|
||||
return this.delegate.getTransactionIsolation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
return this.delegate.isWrapperFor(iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHoldability(int holdability) throws SQLException {
|
||||
this.delegate.setHoldability(holdability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, columnIndexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSchema(String schema) throws SQLException {
|
||||
this.delegate.setSchema(schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(Executor executor) throws SQLException {
|
||||
this.delegate.abort(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
|
||||
this.delegate.setTypeMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NClob createNClob() throws SQLException {
|
||||
return this.delegate.createNClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() throws SQLException {
|
||||
return this.delegate.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAutoCommit() throws SQLException {
|
||||
return this.delegate.getAutoCommit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHoldability() throws SQLException {
|
||||
return this.delegate.getHoldability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
return this.delegate.unwrap(iface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement() throws SQLException {
|
||||
return this.delegate.createStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLXML createSQLXML() throws SQLException {
|
||||
return this.delegate.createSQLXML();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return this.delegate.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getClientInfo() throws SQLException {
|
||||
return this.delegate.getClientInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
|
||||
this.delegate.setNetworkTimeout(executor, milliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchema() throws SQLException {
|
||||
return this.delegate.getSchema();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientInfo(String name) throws SQLException {
|
||||
return this.delegate.getClientInfo(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
|
||||
return this.delegate.createArrayOf(typeName, elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
this.delegate.rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int timeout) throws SQLException {
|
||||
return this.delegate.isValid(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Savepoint savepoint) throws SQLException {
|
||||
this.delegate.rollback(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
|
||||
this.delegate.releaseSavepoint(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql) throws SQLException {
|
||||
return this.delegate.prepareCall(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNetworkTimeout() throws SQLException {
|
||||
return this.delegate.getNetworkTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return this.delegate.createStatement(resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) throws SQLException {
|
||||
this.delegate.setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, columnNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(String name, String value) throws java.sql.SQLClientInfoException {
|
||||
this.delegate.setClientInfo(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint() throws SQLException {
|
||||
return this.delegate.setSavepoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint(String name) throws SQLException {
|
||||
return this.delegate.setSavepoint(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clob createClob() throws SQLException {
|
||||
return this.delegate.createClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||
return this.delegate.prepareStatement(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> getTypeMap() throws SQLException {
|
||||
return this.delegate.getTypeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||
this.delegate.setAutoCommit(autoCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(Properties properties) throws java.sql.SQLClientInfoException {
|
||||
this.delegate.setClientInfo(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
return this.delegate.getWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionIsolation(int level) throws SQLException {
|
||||
this.delegate.setTransactionIsolation(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
return this.delegate.getMetaData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCatalog(String catalog) throws SQLException {
|
||||
this.delegate.setCatalog(catalog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
this.delegate.clearWarnings();
|
||||
/**
|
||||
* Creates a {@link NonClosableConnection} that delegates calls to the given {@link Connection}.
|
||||
*
|
||||
* @param connection the connection to wrap
|
||||
* @return a non closable connection
|
||||
*/
|
||||
static NonClosableConnection wrap(Connection connection) {
|
||||
return (NonClosableConnection) Proxy.newProxyInstance(
|
||||
NonClosableConnection.class.getClassLoader(),
|
||||
new Class[]{NonClosableConnection.class},
|
||||
(proxy, method, args) -> {
|
||||
|
||||
// block calls directly to #close
|
||||
if (method.getName().equals("close")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// proxy calls to #shutdown to the real #close method
|
||||
if (method.getName().equals("shutdown")) {
|
||||
connection.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
// delegate all other calls
|
||||
return method.invoke(connection, args);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually {@link #close() closes} the underlying connection.
|
||||
*/
|
||||
void shutdown();
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public class SQLiteConnectionFactory extends FlatfileConnectionFactory {
|
||||
if (this.connection == null || this.connection.isClosed()) {
|
||||
Connection connection = createConnection("jdbc:sqlite:" + this.file.getAbsolutePath());
|
||||
if (connection != null) {
|
||||
this.connection = new NonClosableConnection(connection);
|
||||
this.connection = NonClosableConnection.wrap(connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,204 +25,116 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage.wrappings;
|
||||
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.api.event.cause.DeletionCause;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.api.delegates.model.ApiStorage;
|
||||
import me.lucko.luckperms.common.buffers.Buffer;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.storage.Storage;
|
||||
import me.lucko.luckperms.common.storage.dao.AbstractDao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A storage wrapping that passes save tasks through a buffer
|
||||
*/
|
||||
public class BufferedOutputStorage implements Storage, Runnable {
|
||||
public static BufferedOutputStorage wrap(Storage storage, long flushTime) {
|
||||
return new BufferedOutputStorage(storage, flushTime);
|
||||
public interface BufferedOutputStorage extends Storage {
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link BufferedOutputStorage} which delegates called to the given
|
||||
* {@link Storage} instance.
|
||||
*
|
||||
* @param delegate the delegate storage impl
|
||||
* @param flushTime default flush time for the buffer. See {@link Buffer#flush(long)}
|
||||
* @return the new buffered storage instance
|
||||
*/
|
||||
static BufferedOutputStorage wrap(Storage delegate, long flushTime) {
|
||||
// create a buffer handler - we pass the unwrapped delegate here.
|
||||
StorageBuffer buffer = new StorageBuffer(delegate, flushTime);
|
||||
|
||||
// create and return a proxy instance which directs save calls through the buffer
|
||||
return (BufferedOutputStorage) Proxy.newProxyInstance(
|
||||
BufferedOutputStorage.class.getClassLoader(),
|
||||
new Class[]{BufferedOutputStorage.class},
|
||||
(proxy, method, args) -> {
|
||||
// run save methods through the buffer instance
|
||||
switch (method.getName()) {
|
||||
case "saveUser":
|
||||
return buffer.saveUser((User) args[0]);
|
||||
case "saveGroup":
|
||||
return buffer.saveGroup((Group) args[0]);
|
||||
case "saveTrack":
|
||||
return buffer.saveTrack((Track) args[0]);
|
||||
}
|
||||
|
||||
// provide implementation of #noBuffer
|
||||
if (method.getName().equals("noBuffer")) {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
// provide implementation of #buffer
|
||||
if (method.getName().equals("buffer")) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// flush the buffer on shutdown
|
||||
if (method.getName().equals("shutdown")) {
|
||||
buffer.forceFlush();
|
||||
// ...and then delegate
|
||||
}
|
||||
|
||||
// delegate the call
|
||||
return method.invoke(delegate, args);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private final Storage delegate;
|
||||
/**
|
||||
* Gets the buffer behind this instance
|
||||
*
|
||||
* @return the buffer
|
||||
*/
|
||||
StorageBuffer buffer();
|
||||
|
||||
private final long flushTime;
|
||||
final class StorageBuffer implements Runnable {
|
||||
private final long flushTime;
|
||||
|
||||
private final Buffer<User, Void> userOutputBuffer = Buffer.of(user -> BufferedOutputStorage.this.delegate.saveUser(user).join());
|
||||
private final Buffer<Group, Void> groupOutputBuffer = Buffer.of(group -> BufferedOutputStorage.this.delegate.saveGroup(group).join());
|
||||
private final Buffer<Track, Void> trackOutputBuffer = Buffer.of(track -> BufferedOutputStorage.this.delegate.saveTrack(track).join());
|
||||
private final Buffer<User, Void> userOutputBuffer;
|
||||
private final Buffer<Group, Void> groupOutputBuffer;
|
||||
private final Buffer<Track, Void> trackOutputBuffer;
|
||||
|
||||
private BufferedOutputStorage(Storage delegate, long flushTime) {
|
||||
this.delegate = delegate;
|
||||
this.flushTime = flushTime;
|
||||
private StorageBuffer(Storage delegate, long flushTime) {
|
||||
this.flushTime = flushTime;
|
||||
this.userOutputBuffer = Buffer.of(user -> delegate.saveUser(user).join());
|
||||
this.groupOutputBuffer = Buffer.of(group -> delegate.saveGroup(group).join());
|
||||
this.trackOutputBuffer = Buffer.of(track -> delegate.saveTrack(track).join());
|
||||
}
|
||||
|
||||
public void run() {
|
||||
flush(this.flushTime);
|
||||
}
|
||||
|
||||
public void forceFlush() {
|
||||
flush(-1);
|
||||
}
|
||||
|
||||
public void flush(long flushTime) {
|
||||
this.userOutputBuffer.flush(flushTime);
|
||||
this.groupOutputBuffer.flush(flushTime);
|
||||
this.trackOutputBuffer.flush(flushTime);
|
||||
}
|
||||
|
||||
// copy the required implementation methods from the Storage interface
|
||||
|
||||
private CompletableFuture<Void> saveUser(User user) {
|
||||
return this.userOutputBuffer.enqueue(user);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> saveGroup(Group group) {
|
||||
return this.groupOutputBuffer.enqueue(group);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> saveTrack(Track track) {
|
||||
return this.trackOutputBuffer.enqueue(track);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
flush(this.flushTime);
|
||||
}
|
||||
|
||||
public void forceFlush() {
|
||||
flush(-1);
|
||||
}
|
||||
|
||||
public void flush(long flushTime) {
|
||||
this.userOutputBuffer.flush(flushTime);
|
||||
this.groupOutputBuffer.flush(flushTime);
|
||||
this.trackOutputBuffer.flush(flushTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Storage noBuffer() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
forceFlush();
|
||||
this.delegate.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveUser(User user) {
|
||||
return this.userOutputBuffer.enqueue(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveGroup(Group group) {
|
||||
return this.groupOutputBuffer.enqueue(group);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveTrack(Track track) {
|
||||
return this.trackOutputBuffer.enqueue(track);
|
||||
}
|
||||
|
||||
// delegate
|
||||
|
||||
@Override
|
||||
public AbstractDao getDao() {
|
||||
return this.delegate.getDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Set<UUID>> getUniqueUsers() {
|
||||
return this.delegate.getUniqueUsers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<Track>> loadTrack(String name) {
|
||||
return this.delegate.loadTrack(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<HeldPermission<UUID>>> getUsersWithPermission(String permission) {
|
||||
return this.delegate.getUsersWithPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<HeldPermission<String>>> getGroupsWithPermission(String permission) {
|
||||
return this.delegate.getGroupsWithPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
return this.delegate.applyBulkUpdate(bulkUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveUUIDData(UUID uuid, String username) {
|
||||
return this.delegate.saveUUIDData(uuid, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Group> createAndLoadGroup(String name, CreationCause cause) {
|
||||
return this.delegate.createAndLoadGroup(name, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMeta() {
|
||||
return this.delegate.getMeta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteGroup(Group group, DeletionCause cause) {
|
||||
return this.delegate.deleteGroup(group, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<UUID> getUUID(String username) {
|
||||
return this.delegate.getUUID(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<User> loadUser(UUID uuid, String username) {
|
||||
return this.delegate.loadUser(uuid, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Track> createAndLoadTrack(String name, CreationCause cause) {
|
||||
return this.delegate.createAndLoadTrack(name, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Log> getLog() {
|
||||
return this.delegate.getLog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiStorage getApiDelegate() {
|
||||
return this.delegate.getApiDelegate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> getName(UUID uuid) {
|
||||
return this.delegate.getName(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.delegate.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> loadAllTracks() {
|
||||
return this.delegate.loadAllTracks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.delegate.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteTrack(Track track, DeletionCause cause) {
|
||||
return this.delegate.deleteTrack(track, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> logAction(LogEntry entry) {
|
||||
return this.delegate.logAction(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> loadAllGroups() {
|
||||
return this.delegate.loadAllGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<Group>> loadGroup(String name) {
|
||||
return this.delegate.loadGroup(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,294 +25,70 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage.wrappings;
|
||||
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.api.event.cause.DeletionCause;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.api.delegates.model.ApiStorage;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.storage.Storage;
|
||||
import me.lucko.luckperms.common.storage.dao.AbstractDao;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* A storage wrapping that ensures all tasks are completed before
|
||||
* {@link Storage#shutdown()} is called.
|
||||
* A storage wrapping that ensures all tasks are completed before {@link Storage#shutdown()} is called.
|
||||
*/
|
||||
public class PhasedStorage implements Storage {
|
||||
public static PhasedStorage wrap(Storage storage) {
|
||||
return new PhasedStorage(storage);
|
||||
}
|
||||
public interface PhasedStorage extends Storage {
|
||||
|
||||
private final Storage delegate;
|
||||
private final Phaser phaser = new Phaser();
|
||||
/**
|
||||
* Creates a new instance of {@link PhasedStorage} which delegates called to the given
|
||||
* {@link Storage} instance.
|
||||
*
|
||||
* @param delegate the delegate storage impl
|
||||
* @return the new phased storage instance
|
||||
*/
|
||||
static PhasedStorage wrap(Storage delegate) {
|
||||
// create a new phaser to be used by the instance
|
||||
Phaser phaser = new Phaser();
|
||||
|
||||
private PhasedStorage(Storage delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
// create and return a proxy instance which directs save calls through the phaser
|
||||
return (PhasedStorage) Proxy.newProxyInstance(
|
||||
PhasedStorage.class.getClassLoader(),
|
||||
new Class[]{PhasedStorage.class},
|
||||
(proxy, method, args) -> {
|
||||
|
||||
@Override
|
||||
public AbstractDao getDao() {
|
||||
return this.delegate.getDao();
|
||||
}
|
||||
// provide implementation of #noBuffer
|
||||
if (method.getName().equals("noBuffer")) {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiStorage getApiDelegate() {
|
||||
return this.delegate.getApiDelegate();
|
||||
}
|
||||
// direct delegation
|
||||
switch (method.getName()) {
|
||||
case "getDao":
|
||||
case "getApiDelegate":
|
||||
case "getName":
|
||||
case "init":
|
||||
case "getMeta":
|
||||
return method.invoke(proxy, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.delegate.getName();
|
||||
}
|
||||
// await the phaser on shutdown
|
||||
if (method.getName().equals("shutdown")) {
|
||||
try {
|
||||
phaser.awaitAdvanceInterruptibly(phaser.getPhase(), 10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | TimeoutException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Storage noBuffer() {
|
||||
return this;
|
||||
}
|
||||
delegate.shutdown();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.delegate.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// Wait for other threads to finish.
|
||||
try {
|
||||
this.phaser.awaitAdvanceInterruptibly(this.phaser.getPhase(), 10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException | TimeoutException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
this.delegate.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMeta() {
|
||||
return this.delegate.getMeta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> logAction(LogEntry entry) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.logAction(entry);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Log> getLog() {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.getLog();
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> applyBulkUpdate(BulkUpdate bulkUpdate) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.applyBulkUpdate(bulkUpdate);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<User> loadUser(UUID uuid, String username) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.loadUser(uuid, username);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveUser(User user) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.saveUser(user);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Set<UUID>> getUniqueUsers() {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.getUniqueUsers();
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<HeldPermission<UUID>>> getUsersWithPermission(String permission) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.getUsersWithPermission(permission);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Group> createAndLoadGroup(String name, CreationCause cause) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.createAndLoadGroup(name, cause);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<Group>> loadGroup(String name) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.loadGroup(name);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> loadAllGroups() {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.loadAllGroups();
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveGroup(Group group) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.saveGroup(group);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteGroup(Group group, DeletionCause cause) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.deleteGroup(group, cause);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<HeldPermission<String>>> getGroupsWithPermission(String permission) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.getGroupsWithPermission(permission);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Track> createAndLoadTrack(String name, CreationCause cause) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.createAndLoadTrack(name, cause);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<Track>> loadTrack(String name) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.loadTrack(name);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> loadAllTracks() {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.loadAllTracks();
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveTrack(Track track) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.saveTrack(track);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteTrack(Track track, DeletionCause cause) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.deleteTrack(track, cause);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> saveUUIDData(UUID uuid, String username) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.saveUUIDData(uuid, username);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<UUID> getUUID(String username) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.getUUID(username);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<String> getName(UUID uuid) {
|
||||
this.phaser.register();
|
||||
try {
|
||||
return this.delegate.getName(uuid);
|
||||
} finally {
|
||||
this.phaser.arriveAndDeregister();
|
||||
}
|
||||
// for all other methods, run the call via the phaser
|
||||
phaser.register();
|
||||
try {
|
||||
return method.invoke(delegate, args);
|
||||
} finally {
|
||||
phaser.arriveAndDeregister();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user