Replace huge forwarding classes with java Proxy instances

This commit is contained in:
Luck 2018-03-18 17:36:07 +00:00
parent da797f154d
commit b8da286f64
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
6 changed files with 188 additions and 757 deletions

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
/**
* 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;
}
@Override
public void close() {
// proxy calls to #shutdown to the real #close method
if (method.getName().equals("shutdown")) {
connection.close();
return null;
}
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();
// delegate all other calls
return method.invoke(connection, args);
}
);
}
/**
* Actually {@link #close() closes} the underlying connection.
*/
void shutdown();
}

View File

@ -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);
}
}

View File

@ -25,49 +25,90 @@
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]);
}
private final Storage delegate;
// 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);
}
);
}
/**
* Gets the buffer behind this instance
*
* @return the buffer
*/
StorageBuffer buffer();
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;
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());
}
@Override
public void run() {
flush(this.flushTime);
}
@ -82,147 +123,18 @@ public class BufferedOutputStorage implements Storage, Runnable {
this.trackOutputBuffer.flush(flushTime);
}
@Override
public Storage noBuffer() {
return this.delegate;
}
// copy the required implementation methods from the Storage interface
@Override
public void shutdown() {
forceFlush();
this.delegate.shutdown();
}
@Override
public CompletableFuture<Void> saveUser(User user) {
private CompletableFuture<Void> saveUser(User user) {
return this.userOutputBuffer.enqueue(user);
}
@Override
public CompletableFuture<Void> saveGroup(Group group) {
private CompletableFuture<Void> saveGroup(Group group) {
return this.groupOutputBuffer.enqueue(group);
}
@Override
public CompletableFuture<Void> saveTrack(Track track) {
private 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);
}
}

View File

@ -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 {
/**
* 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();
// 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) -> {
// provide implementation of #noBuffer
if (method.getName().equals("noBuffer")) {
return delegate;
}
private final Storage delegate;
private final Phaser phaser = new Phaser();
private PhasedStorage(Storage delegate) {
this.delegate = delegate;
// direct delegation
switch (method.getName()) {
case "getDao":
case "getApiDelegate":
case "getName":
case "init":
case "getMeta":
return method.invoke(proxy, args);
}
@Override
public AbstractDao getDao() {
return this.delegate.getDao();
}
@Override
public ApiStorage getApiDelegate() {
return this.delegate.getApiDelegate();
}
@Override
public String getName() {
return this.delegate.getName();
}
@Override
public Storage noBuffer() {
return this;
}
@Override
public void init() {
this.delegate.init();
}
@Override
public void shutdown() {
// Wait for other threads to finish.
// await the phaser on shutdown
if (method.getName().equals("shutdown")) {
try {
this.phaser.awaitAdvanceInterruptibly(this.phaser.getPhase(), 10, TimeUnit.SECONDS);
phaser.awaitAdvanceInterruptibly(phaser.getPhase(), 10, TimeUnit.SECONDS);
} catch (InterruptedException | TimeoutException e) {
e.printStackTrace();
}
this.delegate.shutdown();
delegate.shutdown();
return null;
}
@Override
public Map<String, String> getMeta() {
return this.delegate.getMeta();
}
@Override
public CompletableFuture<Void> logAction(LogEntry entry) {
this.phaser.register();
// for all other methods, run the call via the phaser
phaser.register();
try {
return this.delegate.logAction(entry);
return method.invoke(delegate, args);
} finally {
this.phaser.arriveAndDeregister();
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();
}
);
}
}