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 class AbstractStorage implements Storage {
|
||||||
public static Storage create(LuckPermsPlugin plugin, AbstractDao backing) {
|
public static Storage create(LuckPermsPlugin plugin, AbstractDao backing) {
|
||||||
|
// make a base implementation
|
||||||
Storage base = new AbstractStorage(plugin, backing);
|
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);
|
BufferedOutputStorage buffered = BufferedOutputStorage.wrap(phased, 250L);
|
||||||
plugin.getBootstrap().getScheduler().asyncRepeating(buffered, 2L);
|
plugin.getBootstrap().getScheduler().asyncRepeating(buffered.buffer(), 2L);
|
||||||
return buffered;
|
return buffered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
|||||||
if (this.connection == null || this.connection.isClosed()) {
|
if (this.connection == null || this.connection.isClosed()) {
|
||||||
Connection connection = this.driver.connect("jdbc:h2:" + this.file.getAbsolutePath(), new Properties());
|
Connection connection = this.driver.connect("jdbc:h2:" + this.file.getAbsolutePath(), new Properties());
|
||||||
if (connection != null) {
|
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;
|
package me.lucko.luckperms.common.storage.dao.sql.connection.file;
|
||||||
|
|
||||||
import java.sql.Array;
|
import java.lang.reflect.Proxy;
|
||||||
import java.sql.Blob;
|
|
||||||
import java.sql.CallableStatement;
|
|
||||||
import java.sql.Clob;
|
|
||||||
import java.sql.Connection;
|
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;
|
* Creates a {@link NonClosableConnection} that delegates calls to the given {@link Connection}.
|
||||||
}
|
*
|
||||||
|
* @param connection the connection to wrap
|
||||||
@Override
|
* @return a non closable connection
|
||||||
public void close() {
|
*/
|
||||||
|
static NonClosableConnection wrap(Connection connection) {
|
||||||
}
|
return (NonClosableConnection) Proxy.newProxyInstance(
|
||||||
|
NonClosableConnection.class.getClassLoader(),
|
||||||
public void shutdown() throws SQLException {
|
new Class[]{NonClosableConnection.class},
|
||||||
this.delegate.close();
|
(proxy, method, args) -> {
|
||||||
}
|
|
||||||
|
// block calls directly to #close
|
||||||
// delegate
|
if (method.getName().equals("close")) {
|
||||||
|
return null;
|
||||||
@Override
|
}
|
||||||
public String getCatalog() throws SQLException {
|
|
||||||
return this.delegate.getCatalog();
|
// proxy calls to #shutdown to the real #close method
|
||||||
}
|
if (method.getName().equals("shutdown")) {
|
||||||
|
connection.close();
|
||||||
@Override
|
return null;
|
||||||
public void commit() throws SQLException {
|
}
|
||||||
this.delegate.commit();
|
|
||||||
}
|
// delegate all other calls
|
||||||
|
return method.invoke(connection, args);
|
||||||
@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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()) {
|
if (this.connection == null || this.connection.isClosed()) {
|
||||||
Connection connection = createConnection("jdbc:sqlite:" + this.file.getAbsolutePath());
|
Connection connection = createConnection("jdbc:sqlite:" + this.file.getAbsolutePath());
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
this.connection = new NonClosableConnection(connection);
|
this.connection = NonClosableConnection.wrap(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,204 +25,116 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.storage.wrappings;
|
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.buffers.Buffer;
|
||||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
|
||||||
import me.lucko.luckperms.common.model.Group;
|
import me.lucko.luckperms.common.model.Group;
|
||||||
import me.lucko.luckperms.common.model.Track;
|
import me.lucko.luckperms.common.model.Track;
|
||||||
import me.lucko.luckperms.common.model.User;
|
import me.lucko.luckperms.common.model.User;
|
||||||
import me.lucko.luckperms.common.storage.Storage;
|
import me.lucko.luckperms.common.storage.Storage;
|
||||||
import me.lucko.luckperms.common.storage.dao.AbstractDao;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A storage wrapping that passes save tasks through a buffer
|
* A storage wrapping that passes save tasks through a buffer
|
||||||
*/
|
*/
|
||||||
public class BufferedOutputStorage implements Storage, Runnable {
|
public interface BufferedOutputStorage extends Storage {
|
||||||
public static BufferedOutputStorage wrap(Storage storage, long flushTime) {
|
|
||||||
return new BufferedOutputStorage(storage, flushTime);
|
/**
|
||||||
|
* 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<User, Void> userOutputBuffer;
|
||||||
private final Buffer<Group, Void> groupOutputBuffer = Buffer.of(group -> BufferedOutputStorage.this.delegate.saveGroup(group).join());
|
private final Buffer<Group, Void> groupOutputBuffer;
|
||||||
private final Buffer<Track, Void> trackOutputBuffer = Buffer.of(track -> BufferedOutputStorage.this.delegate.saveTrack(track).join());
|
private final Buffer<Track, Void> trackOutputBuffer;
|
||||||
|
|
||||||
private BufferedOutputStorage(Storage delegate, long flushTime) {
|
private StorageBuffer(Storage delegate, long flushTime) {
|
||||||
this.delegate = delegate;
|
this.flushTime = 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;
|
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.Storage;
|
||||||
import me.lucko.luckperms.common.storage.dao.AbstractDao;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Phaser;
|
import java.util.concurrent.Phaser;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A storage wrapping that ensures all tasks are completed before
|
* A storage wrapping that ensures all tasks are completed before {@link Storage#shutdown()} is called.
|
||||||
* {@link Storage#shutdown()} is called.
|
|
||||||
*/
|
*/
|
||||||
public class PhasedStorage implements Storage {
|
public interface PhasedStorage extends Storage {
|
||||||
public static PhasedStorage wrap(Storage storage) {
|
|
||||||
return new PhasedStorage(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) {
|
// create and return a proxy instance which directs save calls through the phaser
|
||||||
this.delegate = delegate;
|
return (PhasedStorage) Proxy.newProxyInstance(
|
||||||
}
|
PhasedStorage.class.getClassLoader(),
|
||||||
|
new Class[]{PhasedStorage.class},
|
||||||
|
(proxy, method, args) -> {
|
||||||
|
|
||||||
@Override
|
// provide implementation of #noBuffer
|
||||||
public AbstractDao getDao() {
|
if (method.getName().equals("noBuffer")) {
|
||||||
return this.delegate.getDao();
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// direct delegation
|
||||||
public ApiStorage getApiDelegate() {
|
switch (method.getName()) {
|
||||||
return this.delegate.getApiDelegate();
|
case "getDao":
|
||||||
}
|
case "getApiDelegate":
|
||||||
|
case "getName":
|
||||||
|
case "init":
|
||||||
|
case "getMeta":
|
||||||
|
return method.invoke(proxy, args);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
// await the phaser on shutdown
|
||||||
public String getName() {
|
if (method.getName().equals("shutdown")) {
|
||||||
return this.delegate.getName();
|
try {
|
||||||
}
|
phaser.awaitAdvanceInterruptibly(phaser.getPhase(), 10, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException | TimeoutException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
delegate.shutdown();
|
||||||
public Storage noBuffer() {
|
return null;
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// for all other methods, run the call via the phaser
|
||||||
public void init() {
|
phaser.register();
|
||||||
this.delegate.init();
|
try {
|
||||||
}
|
return method.invoke(delegate, args);
|
||||||
|
} finally {
|
||||||
@Override
|
phaser.arriveAndDeregister();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user