mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-10-04 09:27:27 +02:00
All database transactions are now performed by a single thread.
- Added a Database Access Lock object - Access log lets OperationCriticalTransactions through - Transactions skip query access log check - executeTransaction returns a Future to allow easier synchronization - ServerInfo waits for the server to be registered. This could lead to issues if a new server is registering to old database. It should not be too big of an issue since no patches need to be applied on first enable of the database. - Added database states: CLOSED <-> INITIALIZING -> OPEN -> CLOSED These two changes allow restricting queries to the database until the database has properly initialized (Schema is in correct format) - Removed SQLDB as a Patch class variable Tests use Guava direct thread executor on the database to reduce concurrency issues during tests. Another option would be to wait for each transaction.
This commit is contained in:
parent
2b9e407816
commit
8870e034e1
@ -31,6 +31,7 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Manages Server information on the Bungee instance.
|
||||
@ -70,8 +71,10 @@ public class BungeeServerInfo extends ServerInfo {
|
||||
} else {
|
||||
server = registerBungeeInfo(database);
|
||||
}
|
||||
} catch (DBOpException e) {
|
||||
} catch (DBOpException | ExecutionException e) {
|
||||
throw new EnableException("Failed to read Server information from Database.");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return server;
|
||||
}
|
||||
@ -93,12 +96,13 @@ public class BungeeServerInfo extends ServerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
private Server registerBungeeInfo(Database db) throws EnableException {
|
||||
private Server registerBungeeInfo(Database db) throws EnableException, ExecutionException, InterruptedException {
|
||||
UUID serverUUID = generateNewUUID();
|
||||
String accessAddress = webServer.get().getAccessAddress();
|
||||
|
||||
Server proxy = new Server(-1, serverUUID, "BungeeCord", accessAddress, serverProperties.getMaxPlayers());
|
||||
db.executeTransaction(new StoreServerInformationTransaction(proxy));
|
||||
db.executeTransaction(new StoreServerInformationTransaction(proxy))
|
||||
.get();
|
||||
|
||||
Optional<Server> proxyInfo = db.query(ServerQueries.fetchProxyServerInformation());
|
||||
if (proxyInfo.isPresent()) {
|
||||
|
@ -17,11 +17,13 @@
|
||||
package com.djrapitops.plan;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.EnableException;
|
||||
import com.djrapitops.plan.db.SQLiteDB;
|
||||
import com.djrapitops.plan.system.PlanSystem;
|
||||
import com.djrapitops.plan.system.database.DBSystem;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.ProxySettings;
|
||||
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@ -62,7 +64,9 @@ public class BungeeSystemTest {
|
||||
config.set(ProxySettings.IP, "8.8.8.8");
|
||||
|
||||
DBSystem dbSystem = bungeeSystem.getDatabaseSystem();
|
||||
dbSystem.setActiveDatabase(dbSystem.getSqLiteFactory().usingDefaultFile());
|
||||
SQLiteDB db = dbSystem.getSqLiteFactory().usingDefaultFile();
|
||||
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
dbSystem.setActiveDatabase(db);
|
||||
|
||||
bungeeSystem.enable();
|
||||
} finally {
|
||||
@ -82,7 +86,9 @@ public class BungeeSystemTest {
|
||||
config.set(ProxySettings.IP, "0.0.0.0");
|
||||
|
||||
DBSystem dbSystem = bungeeSystem.getDatabaseSystem();
|
||||
dbSystem.setActiveDatabase(dbSystem.getSqLiteFactory().usingDefaultFile());
|
||||
SQLiteDB db = dbSystem.getSqLiteFactory().usingDefaultFile();
|
||||
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
dbSystem.setActiveDatabase(db);
|
||||
|
||||
bungeeSystem.enable();
|
||||
assertTrue(bungeeSystem.isEnabled());
|
||||
|
@ -77,7 +77,6 @@ public class ShutdownHook extends Thread {
|
||||
public void run() {
|
||||
try {
|
||||
Map<UUID, Session> activeSessions = SessionCache.getActiveSessions();
|
||||
long now = System.currentTimeMillis();
|
||||
prepareSessionsForStorage(activeSessions, System.currentTimeMillis());
|
||||
saveActiveSessions(activeSessions);
|
||||
} catch (IllegalStateException ignored) {
|
||||
@ -91,7 +90,7 @@ public class ShutdownHook extends Thread {
|
||||
|
||||
private void saveActiveSessions(Map<UUID, Session> activeSessions) throws DBInitException {
|
||||
Database database = dbSystem.getDatabase();
|
||||
if (!database.isOpen()) {
|
||||
if (database.getState() == Database.State.CLOSED) {
|
||||
// Ensure that database is not closed when performing the transaction.
|
||||
database.init();
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ package com.djrapitops.plan.api.exceptions.database;
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class DBInitException extends FatalDBException {
|
||||
public class DBInitException extends DBException {
|
||||
|
||||
public DBInitException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
|
@ -16,15 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.api.exceptions.database;
|
||||
|
||||
public class FatalDBException extends DBException {
|
||||
|
||||
public FatalDBException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public FatalDBException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
public class FatalDBException extends DBOpException {
|
||||
|
||||
public FatalDBException(String message) {
|
||||
super(message);
|
||||
|
@ -84,7 +84,7 @@ public class ManageHotSwapCommand extends CommandNode {
|
||||
Database database = dbSystem.getActiveDatabaseByName(dbName);
|
||||
database.init();
|
||||
|
||||
if (!database.isOpen()) {
|
||||
if (database.getState() == Database.State.CLOSED) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -25,11 +25,21 @@ package com.djrapitops.plan.db;
|
||||
*/
|
||||
public abstract class AbstractDatabase implements Database {
|
||||
|
||||
protected volatile boolean open = false;
|
||||
protected DBAccessLock accessLock;
|
||||
private State state;
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
public AbstractDatabase() {
|
||||
state = State.CLOSED;
|
||||
accessLock = new DBAccessLock(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
accessLock.operabilityChanged();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.db;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction;
|
||||
|
||||
/**
|
||||
* Database Lock that prevents queries and transactions from taking place before database schema is ready.
|
||||
* <p>
|
||||
* - OperationCriticalTransactions pass through the Access lock without blocking to allow the initial transactions.
|
||||
* - Queries inside Transactions skip access log to allow OperationCriticalTransactions perform queries.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class DBAccessLock {
|
||||
|
||||
private final Database database;
|
||||
|
||||
private final Object lockObject;
|
||||
|
||||
public DBAccessLock(Database database) {
|
||||
this.database = database;
|
||||
this.lockObject = new Object();
|
||||
}
|
||||
|
||||
public void checkAccess() {
|
||||
checkAccess(false);
|
||||
}
|
||||
|
||||
public void checkAccess(Transaction transaction) {
|
||||
checkAccess(transaction instanceof OperationCriticalTransaction);
|
||||
}
|
||||
|
||||
private void checkAccess(boolean isOperationCriticalTransaction) {
|
||||
if (isOperationCriticalTransaction) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
while (database.getState() != Database.State.OPEN) {
|
||||
synchronized (lockObject) {
|
||||
lockObject.wait();
|
||||
if (database.getState() == Database.State.CLOSED) {
|
||||
throw new DBOpException("Database failed to open, Query has failed. (This exception is necessary to not keep query threads waiting)");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
public void operabilityChanged() {
|
||||
synchronized (lockObject) {
|
||||
lockObject.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,8 @@ import com.djrapitops.plan.api.exceptions.database.DBInitException;
|
||||
import com.djrapitops.plan.db.access.Query;
|
||||
import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Interface for interacting with a Plan SQL database.
|
||||
*
|
||||
@ -32,8 +34,6 @@ public interface Database {
|
||||
|
||||
void close() throws DBException;
|
||||
|
||||
boolean isOpen();
|
||||
|
||||
/**
|
||||
* Execute an SQL Query statement to get a result.
|
||||
* <p>
|
||||
@ -49,8 +49,9 @@ public interface Database {
|
||||
* Execute an SQL Transaction.
|
||||
*
|
||||
* @param transaction Transaction to execute.
|
||||
* @return Future that is finished when the transaction has been executed.
|
||||
*/
|
||||
void executeTransaction(Transaction transaction);
|
||||
Future<?> executeTransaction(Transaction transaction);
|
||||
|
||||
/**
|
||||
* Used to get the {@code DBType} of the Database
|
||||
@ -58,9 +59,16 @@ public interface Database {
|
||||
* @return the {@code DBType}
|
||||
* @see DBType
|
||||
*/
|
||||
@Deprecated
|
||||
DBType getType();
|
||||
|
||||
@Deprecated
|
||||
void scheduleClean(long delay);
|
||||
|
||||
State getState();
|
||||
|
||||
enum State {
|
||||
CLOSED,
|
||||
INITIALIZING,
|
||||
OPEN
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,7 @@ import com.djrapitops.plan.system.locale.Locale;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
|
||||
import com.djrapitops.plan.utilities.MiscUtils;
|
||||
import com.djrapitops.plugin.benchmarking.Timings;
|
||||
import com.djrapitops.plugin.logging.L;
|
||||
import com.djrapitops.plan.utilities.java.ThrowableUtils;
|
||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||
import com.djrapitops.plugin.logging.error.ErrorHandler;
|
||||
import com.djrapitops.plugin.task.PluginTask;
|
||||
@ -61,10 +60,9 @@ public class H2DB extends SQLDB {
|
||||
NetworkContainer.Factory networkContainerFactory,
|
||||
RunnableFactory runnableFactory,
|
||||
PluginLogger logger,
|
||||
Timings timings,
|
||||
ErrorHandler errorHandler
|
||||
) {
|
||||
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, timings, errorHandler);
|
||||
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, errorHandler);
|
||||
dbName = databaseFile.getName();
|
||||
this.databaseFile = databaseFile;
|
||||
}
|
||||
@ -77,7 +75,7 @@ public class H2DB extends SQLDB {
|
||||
throw new DBInitException(e);
|
||||
}
|
||||
|
||||
execute("SET REFERENTIAL_INTEGRITY FALSE");
|
||||
// TODO Figure out if execute("SET REFERENTIAL_INTEGRITY FALSE"); is required
|
||||
startConnectionPingTask();
|
||||
}
|
||||
|
||||
@ -145,22 +143,12 @@ public class H2DB extends SQLDB {
|
||||
stopConnectionPingTask();
|
||||
|
||||
if (connection != null) {
|
||||
logger.debug("H2DB " + dbName + ": Closed Connection");
|
||||
logger.debug("H2 Connection close prompted by: " + ThrowableUtils.findCallerAfterClass(Thread.currentThread().getStackTrace(), H2DB.class));
|
||||
logger.debug("H2 " + dbName + ": Closed Connection");
|
||||
MiscUtils.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(Connection connection) {
|
||||
try {
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
if (!e.getMessage().contains("cannot commit")) {
|
||||
errorHandler.log(L.ERROR, this.getClass(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnToPool(Connection connection) {
|
||||
// Connection pool not in use, no action required.
|
||||
@ -189,7 +177,6 @@ public class H2DB extends SQLDB {
|
||||
private final NetworkContainer.Factory networkContainerFactory;
|
||||
private final RunnableFactory runnableFactory;
|
||||
private final PluginLogger logger;
|
||||
private final Timings timings;
|
||||
private final ErrorHandler errorHandler;
|
||||
private PlanFiles files;
|
||||
|
||||
@ -202,7 +189,6 @@ public class H2DB extends SQLDB {
|
||||
NetworkContainer.Factory networkContainerFactory,
|
||||
RunnableFactory runnableFactory,
|
||||
PluginLogger logger,
|
||||
Timings timings,
|
||||
ErrorHandler errorHandler
|
||||
) {
|
||||
this.locale = locale;
|
||||
@ -212,7 +198,6 @@ public class H2DB extends SQLDB {
|
||||
this.networkContainerFactory = networkContainerFactory;
|
||||
this.runnableFactory = runnableFactory;
|
||||
this.logger = logger;
|
||||
this.timings = timings;
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
@ -228,7 +213,7 @@ public class H2DB extends SQLDB {
|
||||
return new H2DB(databaseFile,
|
||||
locale, config, serverInfo,
|
||||
networkContainerFactory,
|
||||
runnableFactory, logger, timings, errorHandler
|
||||
runnableFactory, logger, errorHandler
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public class MySQLDB extends SQLDB {
|
||||
Timings timings,
|
||||
ErrorHandler errorHandler
|
||||
) {
|
||||
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, pluginLogger, timings, errorHandler);
|
||||
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, pluginLogger, errorHandler);
|
||||
}
|
||||
|
||||
private static synchronized void increment() {
|
||||
@ -154,11 +154,6 @@ public class MySQLDB extends SQLDB {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(Connection connection) {
|
||||
returnToPool(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -18,35 +18,35 @@ package com.djrapitops.plan.db;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBInitException;
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.api.exceptions.database.FatalDBException;
|
||||
import com.djrapitops.plan.data.store.containers.NetworkContainer;
|
||||
import com.djrapitops.plan.db.access.ExecStatement;
|
||||
import com.djrapitops.plan.db.access.Query;
|
||||
import com.djrapitops.plan.db.access.QueryStatement;
|
||||
import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
import com.djrapitops.plan.db.access.transactions.init.CleanTransaction;
|
||||
import com.djrapitops.plan.db.access.transactions.init.CreateIndexTransaction;
|
||||
import com.djrapitops.plan.db.access.transactions.init.CreateTablesTransaction;
|
||||
import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction;
|
||||
import com.djrapitops.plan.db.patches.*;
|
||||
import com.djrapitops.plan.system.locale.Locale;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.PluginSettings;
|
||||
import com.djrapitops.plan.system.settings.paths.TimeSettings;
|
||||
import com.djrapitops.plan.utilities.java.ThrowableUtils;
|
||||
import com.djrapitops.plugin.api.TimeAmount;
|
||||
import com.djrapitops.plugin.benchmarking.Timings;
|
||||
import com.djrapitops.plugin.logging.L;
|
||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||
import com.djrapitops.plugin.logging.error.ErrorHandler;
|
||||
import com.djrapitops.plugin.task.AbsRunnable;
|
||||
import com.djrapitops.plugin.task.PluginTask;
|
||||
import com.djrapitops.plugin.task.RunnableFactory;
|
||||
import com.djrapitops.plugin.utilities.Verify;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@ -63,18 +63,19 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
protected final NetworkContainer.Factory networkContainerFactory;
|
||||
protected final RunnableFactory runnableFactory;
|
||||
protected final PluginLogger logger;
|
||||
protected final Timings timings;
|
||||
protected final ErrorHandler errorHandler;
|
||||
|
||||
private PluginTask dbCleanTask;
|
||||
|
||||
private Supplier<ExecutorService> transactionExecutorServiceProvider;
|
||||
private ExecutorService transactionExecutor;
|
||||
|
||||
public SQLDB(
|
||||
Supplier<UUID> serverUUIDSupplier,
|
||||
Locale locale,
|
||||
PlanConfig config,
|
||||
NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory,
|
||||
PluginLogger logger,
|
||||
Timings timings,
|
||||
ErrorHandler errorHandler
|
||||
) {
|
||||
this.serverUUIDSupplier = serverUUIDSupplier;
|
||||
@ -83,8 +84,9 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
this.networkContainerFactory = networkContainerFactory;
|
||||
this.runnableFactory = runnableFactory;
|
||||
this.logger = logger;
|
||||
this.timings = timings;
|
||||
this.errorHandler = errorHandler;
|
||||
|
||||
this.transactionExecutorServiceProvider = () -> Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Plan " + getClass().getSimpleName() + "-transaction-thread-%d").build());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,13 +101,38 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
*/
|
||||
@Override
|
||||
public void init() throws DBInitException {
|
||||
setOpen(true);
|
||||
List<Runnable> unfinishedTransactions = closeTransactionExecutor(transactionExecutor);
|
||||
this.transactionExecutor = transactionExecutorServiceProvider.get();
|
||||
|
||||
setState(State.INITIALIZING);
|
||||
|
||||
setupDataSource();
|
||||
setupDatabase();
|
||||
|
||||
for (Runnable unfinishedTransaction : unfinishedTransactions) {
|
||||
transactionExecutor.submit(unfinishedTransaction);
|
||||
}
|
||||
|
||||
// If an OperationCriticalTransaction fails open is set to false.
|
||||
// See executeTransaction method below.
|
||||
if (getState() == State.CLOSED) {
|
||||
throw new DBInitException("Failed to set-up Database");
|
||||
}
|
||||
}
|
||||
|
||||
void setOpen(boolean value) {
|
||||
open = value;
|
||||
private List<Runnable> closeTransactionExecutor(ExecutorService transactionExecutor) {
|
||||
if (transactionExecutor == null || transactionExecutor.isShutdown() || transactionExecutor.isTerminated()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
transactionExecutor.shutdown();
|
||||
try {
|
||||
if (!transactionExecutor.awaitTermination(5L, TimeUnit.SECONDS)) {
|
||||
return transactionExecutor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,7 +141,7 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (isOpen()) {
|
||||
if (getState() != State.CLOSED) {
|
||||
executeTransaction(new CleanTransaction(serverUUIDSupplier.get(),
|
||||
config.get(TimeSettings.KEEP_INACTIVE_PLAYERS), logger, locale)
|
||||
);
|
||||
@ -132,27 +159,27 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
|
||||
Patch[] patches() {
|
||||
return new Patch[]{
|
||||
new Version10Patch(this),
|
||||
new GeoInfoLastUsedPatch(this),
|
||||
new SessionAFKTimePatch(this),
|
||||
new KillsServerIDPatch(this),
|
||||
new WorldTimesSeverIDPatch(this),
|
||||
new WorldsServerIDPatch(this),
|
||||
new NicknameLastSeenPatch(this),
|
||||
new VersionTableRemovalPatch(this),
|
||||
new DiskUsagePatch(this),
|
||||
new WorldsOptimizationPatch(this),
|
||||
new WorldTimesOptimizationPatch(this),
|
||||
new KillsOptimizationPatch(this),
|
||||
new SessionsOptimizationPatch(this),
|
||||
new PingOptimizationPatch(this),
|
||||
new NicknamesOptimizationPatch(this),
|
||||
new UserInfoOptimizationPatch(this),
|
||||
new GeoInfoOptimizationPatch(this),
|
||||
new TransferTableRemovalPatch(this),
|
||||
new IPHashPatch(this),
|
||||
new IPAnonPatch(this),
|
||||
new BadAFKThresholdValuePatch(this)
|
||||
new Version10Patch(),
|
||||
new GeoInfoLastUsedPatch(),
|
||||
new SessionAFKTimePatch(),
|
||||
new KillsServerIDPatch(),
|
||||
new WorldTimesSeverIDPatch(),
|
||||
new WorldsServerIDPatch(),
|
||||
new NicknameLastSeenPatch(),
|
||||
new VersionTableRemovalPatch(),
|
||||
new DiskUsagePatch(),
|
||||
new WorldsOptimizationPatch(),
|
||||
new WorldTimesOptimizationPatch(),
|
||||
new KillsOptimizationPatch(),
|
||||
new SessionsOptimizationPatch(),
|
||||
new PingOptimizationPatch(),
|
||||
new NicknamesOptimizationPatch(),
|
||||
new UserInfoOptimizationPatch(),
|
||||
new GeoInfoOptimizationPatch(),
|
||||
new TransferTableRemovalPatch(),
|
||||
new IPHashPatch(),
|
||||
new IPAnonPatch(),
|
||||
new BadAFKThresholdValuePatch()
|
||||
};
|
||||
}
|
||||
|
||||
@ -160,19 +187,19 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
* Ensures connection functions correctly and all tables exist.
|
||||
* <p>
|
||||
* Updates to latest schema.
|
||||
*
|
||||
* @throws DBInitException if something goes wrong.
|
||||
*/
|
||||
private void setupDatabase() throws DBInitException {
|
||||
try {
|
||||
executeTransaction(new CreateTablesTransaction());
|
||||
for (Patch patch : patches()) {
|
||||
executeTransaction(patch);
|
||||
}
|
||||
registerIndexCreationTask();
|
||||
} catch (DBOpException | IllegalArgumentException e) {
|
||||
throw new DBInitException("Failed to set-up Database", e);
|
||||
private void setupDatabase() {
|
||||
executeTransaction(new CreateTablesTransaction());
|
||||
for (Patch patch : patches()) {
|
||||
executeTransaction(patch);
|
||||
}
|
||||
executeTransaction(new OperationCriticalTransaction() {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
if (getState() == State.INITIALIZING) setState(State.OPEN);
|
||||
}
|
||||
});
|
||||
registerIndexCreationTask();
|
||||
}
|
||||
|
||||
private void registerIndexCreationTask() {
|
||||
@ -192,7 +219,8 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
setOpen(false);
|
||||
setState(State.CLOSED);
|
||||
closeTransactionExecutor(transactionExecutor);
|
||||
if (dbCleanTask != null) {
|
||||
dbCleanTask.cancel();
|
||||
}
|
||||
@ -200,100 +228,46 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
|
||||
public abstract Connection getConnection() throws SQLException;
|
||||
|
||||
@Deprecated
|
||||
public abstract void commit(Connection connection);
|
||||
|
||||
public abstract void returnToPool(Connection connection);
|
||||
|
||||
@Deprecated
|
||||
public boolean execute(ExecStatement statement) {
|
||||
if (!isOpen()) {
|
||||
throw new DBOpException("SQL Statement tried to execute while connection closed");
|
||||
}
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = getConnection();
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(statement.getSql())) {
|
||||
return statement.execute(preparedStatement);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw DBOpException.forCause(statement.getSql(), e);
|
||||
} finally {
|
||||
commit(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean execute(String sql) {
|
||||
return execute(new ExecStatement(sql) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) {
|
||||
// Statement is ready for execution.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void executeUnsafe(String... statements) {
|
||||
Verify.nullCheck(statements);
|
||||
for (String statement : statements) {
|
||||
try {
|
||||
execute(statement);
|
||||
} catch (DBOpException e) {
|
||||
if (config.isTrue(PluginSettings.DEV_MODE)) {
|
||||
errorHandler.log(L.ERROR, this.getClass(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void executeBatch(ExecStatement statement) {
|
||||
if (!isOpen()) {
|
||||
throw new DBOpException("SQL Batch tried to execute while connection closed");
|
||||
}
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = getConnection();
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(statement.getSql())) {
|
||||
statement.executeBatch(preparedStatement);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw DBOpException.forCause(statement.getSql(), e);
|
||||
} finally {
|
||||
commit(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public <T> T query(QueryStatement<T> statement) {
|
||||
if (!isOpen()) {
|
||||
throw new DBOpException("SQL Query tried to execute while connection closed");
|
||||
}
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = getConnection();
|
||||
try (PreparedStatement preparedStatement = connection.prepareStatement(statement.getSql())) {
|
||||
return statement.executeQuery(preparedStatement);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw DBOpException.forCause(statement.getSql(), e);
|
||||
} finally {
|
||||
returnToPool(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T query(Query<T> query) {
|
||||
accessLock.checkAccess();
|
||||
return query.executeQuery(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeTransaction(Transaction transaction) {
|
||||
transaction.executeTransaction(this);
|
||||
public Future<?> executeTransaction(Transaction transaction) {
|
||||
if (getState() == State.CLOSED) {
|
||||
throw new DBOpException("Transaction tried to execute although database is closed.");
|
||||
}
|
||||
|
||||
Exception origin = new Exception();
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
accessLock.checkAccess(transaction);
|
||||
transaction.executeTransaction(this);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}, getTransactionExecutor()).handle((obj, throwable) -> {
|
||||
if (throwable == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
if (throwable instanceof FatalDBException) {
|
||||
setState(State.CLOSED);
|
||||
}
|
||||
ThrowableUtils.appendEntryPointToCause(throwable, origin);
|
||||
|
||||
errorHandler.log(L.ERROR, getClass(), throwable);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private ExecutorService getTransactionExecutor() {
|
||||
if (transactionExecutor == null) {
|
||||
transactionExecutor = transactionExecutorServiceProvider.get();
|
||||
}
|
||||
return transactionExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -313,19 +287,11 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
return serverUUIDSupplier;
|
||||
}
|
||||
|
||||
public PlanConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public PluginLogger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public NetworkContainer.Factory getNetworkContainerFactory() {
|
||||
return networkContainerFactory;
|
||||
}
|
||||
|
||||
public void setTransactionExecutorServiceProvider(Supplier<ExecutorService> transactionExecutorServiceProvider) {
|
||||
this.transactionExecutorServiceProvider = transactionExecutorServiceProvider;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import com.djrapitops.plan.system.locale.Locale;
|
||||
import com.djrapitops.plan.system.locale.lang.PluginLang;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.utilities.MiscUtils;
|
||||
import com.djrapitops.plugin.benchmarking.Timings;
|
||||
import com.djrapitops.plan.utilities.java.ThrowableUtils;
|
||||
import com.djrapitops.plugin.logging.L;
|
||||
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||
import com.djrapitops.plugin.logging.error.ErrorHandler;
|
||||
@ -59,10 +59,9 @@ public class SQLiteDB extends SQLDB {
|
||||
NetworkContainer.Factory networkContainerFactory,
|
||||
RunnableFactory runnableFactory,
|
||||
PluginLogger logger,
|
||||
Timings timings,
|
||||
ErrorHandler errorHandler
|
||||
) {
|
||||
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, timings, errorHandler);
|
||||
super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, errorHandler);
|
||||
dbName = databaseFile.getName();
|
||||
this.databaseFile = databaseFile;
|
||||
}
|
||||
@ -144,22 +143,12 @@ public class SQLiteDB extends SQLDB {
|
||||
stopConnectionPingTask();
|
||||
|
||||
if (connection != null) {
|
||||
logger.debug("SQLite Connection close prompted by: " + ThrowableUtils.findCallerAfterClass(Thread.currentThread().getStackTrace(), SQLiteDB.class));
|
||||
logger.debug("SQLite " + dbName + ": Closed Connection");
|
||||
MiscUtils.close(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(Connection connection) {
|
||||
try {
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
if (!e.getMessage().contains("cannot commit")) {
|
||||
errorHandler.log(L.ERROR, this.getClass(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnToPool(Connection connection) {
|
||||
// Connection pool not in use, no action required.
|
||||
@ -188,7 +177,6 @@ public class SQLiteDB extends SQLDB {
|
||||
private final NetworkContainer.Factory networkContainerFactory;
|
||||
private final RunnableFactory runnableFactory;
|
||||
private final PluginLogger logger;
|
||||
private final Timings timings;
|
||||
private final ErrorHandler errorHandler;
|
||||
private PlanFiles files;
|
||||
|
||||
@ -201,7 +189,6 @@ public class SQLiteDB extends SQLDB {
|
||||
NetworkContainer.Factory networkContainerFactory,
|
||||
RunnableFactory runnableFactory,
|
||||
PluginLogger logger,
|
||||
Timings timings,
|
||||
ErrorHandler errorHandler
|
||||
) {
|
||||
this.locale = locale;
|
||||
@ -211,7 +198,6 @@ public class SQLiteDB extends SQLDB {
|
||||
this.networkContainerFactory = networkContainerFactory;
|
||||
this.runnableFactory = runnableFactory;
|
||||
this.logger = logger;
|
||||
this.timings = timings;
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
@ -227,7 +213,7 @@ public class SQLiteDB extends SQLDB {
|
||||
return new SQLiteDB(databaseFile,
|
||||
locale, config, serverInfo,
|
||||
networkContainerFactory,
|
||||
runnableFactory, logger, timings, errorHandler
|
||||
runnableFactory, logger, errorHandler
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ public class BackupCopyTransaction extends RemoveEverythingTransaction {
|
||||
|
||||
@Override
|
||||
protected boolean shouldBeExecuted() {
|
||||
return !sourceDB.equals(db) && sourceDB.isOpen();
|
||||
return !sourceDB.equals(db) && sourceDB.getState() != Database.State.CLOSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,6 +28,7 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Savepoint;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a database transaction.
|
||||
@ -37,11 +38,12 @@ import java.sql.Savepoint;
|
||||
public abstract class Transaction {
|
||||
|
||||
protected SQLDB db; // TODO Make private, this is a quick hack to access some tables while they are in use.
|
||||
protected DBType dbType;
|
||||
|
||||
private Connection connection;
|
||||
private Savepoint savepoint;
|
||||
|
||||
private boolean success;
|
||||
protected boolean success;
|
||||
|
||||
protected Transaction() {
|
||||
success = false;
|
||||
@ -52,8 +54,10 @@ public abstract class Transaction {
|
||||
Verify.isFalse(success, () -> new IllegalStateException("Transaction has already been executed"));
|
||||
|
||||
this.db = db;
|
||||
this.dbType = db.getType();
|
||||
|
||||
if (!shouldBeExecuted()) {
|
||||
success = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -103,7 +107,6 @@ public abstract class Transaction {
|
||||
throw new DBOpException(getClass().getSimpleName() + " finalization failed: " + e.getMessage(), e);
|
||||
}
|
||||
if (db != null) db.returnToPool(connection);
|
||||
db = null;
|
||||
}
|
||||
|
||||
private void handleSavepoint() throws SQLException {
|
||||
@ -119,7 +122,7 @@ public abstract class Transaction {
|
||||
}
|
||||
|
||||
protected <T> T query(Query<T> query) {
|
||||
return db.query(query);
|
||||
return query.executeQuery(db);
|
||||
}
|
||||
|
||||
protected boolean execute(Executable executable) {
|
||||
@ -152,12 +155,12 @@ public abstract class Transaction {
|
||||
transaction.connection = null;
|
||||
}
|
||||
|
||||
protected DBType getDBType() {
|
||||
return db.getType();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void setDb(SQLDB db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
protected UUID getServerUUID() {
|
||||
return db.getServerUUIDSupplier().get();
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.access.transactions.init;
|
||||
|
||||
import com.djrapitops.plan.db.DBType;
|
||||
import com.djrapitops.plan.db.sql.tables.*;
|
||||
|
||||
/**
|
||||
@ -29,7 +28,6 @@ public class CreateTablesTransaction extends OperationCriticalTransaction {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
// DBType is required for SQL parsing, as MySQL and SQLite primary key format differs.
|
||||
DBType dbType = getDBType();
|
||||
|
||||
// Create statements are run in a specific order as some tables have foreign keys,
|
||||
// or had at some point in the past.
|
||||
|
@ -16,6 +16,8 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.access.transactions.init;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.FatalDBException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
|
||||
/**
|
||||
@ -27,4 +29,11 @@ import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
*/
|
||||
public abstract class OperationCriticalTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public void executeTransaction(SQLDB db) {
|
||||
super.executeTransaction(db);
|
||||
if (!success) {
|
||||
throw new FatalDBException(getClass().getSimpleName() + " failed to execute and database can not be opened.");
|
||||
}
|
||||
}
|
||||
}
|
@ -16,12 +16,10 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement;
|
||||
import com.djrapitops.plan.db.sql.tables.SessionsTable;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Patch that resets AFK time of sessions with afk time of length of the session to 0.
|
||||
@ -35,10 +33,6 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public class BadAFKThresholdValuePatch extends Patch {
|
||||
|
||||
public BadAFKThresholdValuePatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return !containsSessionsWithFullAFK();
|
||||
@ -53,7 +47,7 @@ public class BadAFKThresholdValuePatch extends Patch {
|
||||
")) < 5 AND " + SessionsTable.AFK_TIME + "!=0";
|
||||
return query(new HasMoreThanZeroQueryStatement(sql, "found") {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
public void prepare(PreparedStatement statement) {
|
||||
/* Nothing to prepare */
|
||||
}
|
||||
});
|
||||
|
@ -16,15 +16,10 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.TPSTable;
|
||||
|
||||
public class DiskUsagePatch extends Patch {
|
||||
|
||||
public DiskUsagePatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return hasColumn(TPSTable.TABLE_NAME, TPSTable.FREE_DISK);
|
||||
|
@ -16,15 +16,10 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.GeoInfoTable;
|
||||
|
||||
public class GeoInfoLastUsedPatch extends Patch {
|
||||
|
||||
public GeoInfoLastUsedPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return hasColumn(GeoInfoTable.TABLE_NAME, GeoInfoTable.LAST_USED);
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.GeoInfoTable;
|
||||
|
||||
public class GeoInfoOptimizationPatch extends Patch {
|
||||
@ -24,8 +23,7 @@ public class GeoInfoOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public GeoInfoOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public GeoInfoOptimizationPatch() {
|
||||
tableName = GeoInfoTable.TABLE_NAME;
|
||||
tempTableName = "temp_ips";
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.data.container.GeoInfo;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.ExecBatchStatement;
|
||||
import com.djrapitops.plan.db.access.QueryStatement;
|
||||
import com.djrapitops.plan.db.access.queries.objects.GeoInfoQueries;
|
||||
@ -38,8 +37,7 @@ public class IPAnonPatch extends Patch {
|
||||
private String tableName;
|
||||
private String tempTableName;
|
||||
|
||||
public IPAnonPatch(SQLDB db) {
|
||||
super(db);
|
||||
public IPAnonPatch() {
|
||||
tableName = GeoInfoTable.TABLE_NAME;
|
||||
tempTableName = "plan_ips_temp";
|
||||
}
|
||||
@ -68,7 +66,7 @@ public class IPAnonPatch extends Patch {
|
||||
|
||||
@Override
|
||||
protected void applyPatch() {
|
||||
Map<UUID, List<GeoInfo>> allGeoInfo = db.query(GeoInfoQueries.fetchAllGeoInformation());
|
||||
Map<UUID, List<GeoInfo>> allGeoInfo = query(GeoInfoQueries.fetchAllGeoInformation());
|
||||
anonymizeIPs(allGeoInfo);
|
||||
groupHashedIPs();
|
||||
}
|
||||
|
@ -16,15 +16,10 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.GeoInfoTable;
|
||||
|
||||
public class IPHashPatch extends Patch {
|
||||
|
||||
public IPHashPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return hasColumn(GeoInfoTable.TABLE_NAME, GeoInfoTable.IP_HASH);
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.KillsTable;
|
||||
|
||||
public class KillsOptimizationPatch extends Patch {
|
||||
@ -25,8 +24,7 @@ public class KillsOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public KillsOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public KillsOptimizationPatch() {
|
||||
tableName = KillsTable.TABLE_NAME;
|
||||
tempTableName = "temp_kills";
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.ExecBatchStatement;
|
||||
import com.djrapitops.plan.db.access.QueryStatement;
|
||||
import com.djrapitops.plan.db.access.queries.schema.SessionIDServerIDRelationQuery;
|
||||
@ -29,10 +28,6 @@ import java.util.Map;
|
||||
|
||||
public class KillsServerIDPatch extends Patch {
|
||||
|
||||
public KillsServerIDPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
String tableName = KillsTable.TABLE_NAME;
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.data.store.objects.Nickname;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.ExecBatchStatement;
|
||||
import com.djrapitops.plan.db.access.QueryAllStatement;
|
||||
import com.djrapitops.plan.db.sql.parsing.Select;
|
||||
@ -31,10 +30,6 @@ import java.util.*;
|
||||
|
||||
public class NicknameLastSeenPatch extends Patch {
|
||||
|
||||
public NicknameLastSeenPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return hasColumn(NicknamesTable.TABLE_NAME, NicknamesTable.LAST_USED);
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.NicknamesTable;
|
||||
|
||||
public class NicknamesOptimizationPatch extends Patch {
|
||||
@ -25,8 +24,7 @@ public class NicknamesOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public NicknamesOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public NicknamesOptimizationPatch() {
|
||||
tableName = NicknamesTable.TABLE_NAME;
|
||||
tempTableName = "temp_nicknames";
|
||||
}
|
||||
|
@ -18,29 +18,18 @@ package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.DBType;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.queries.schema.H2SchemaQueries;
|
||||
import com.djrapitops.plan.db.access.queries.schema.MySQLSchemaQueries;
|
||||
import com.djrapitops.plan.db.access.queries.schema.SQLiteSchemaQueries;
|
||||
import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction;
|
||||
import com.djrapitops.plan.db.sql.parsing.TableSqlParser;
|
||||
import com.djrapitops.plugin.utilities.Verify;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class Patch extends OperationCriticalTransaction {
|
||||
|
||||
protected final SQLDB db;
|
||||
protected final DBType dbType;
|
||||
private static final String ALTER_TABLE = "ALTER TABLE ";
|
||||
|
||||
public Patch(SQLDB db) {
|
||||
setDb(db);
|
||||
this.db = db;
|
||||
this.dbType = db.getType();
|
||||
}
|
||||
|
||||
public abstract boolean hasBeenApplied();
|
||||
|
||||
protected abstract void applyPatch();
|
||||
@ -57,11 +46,6 @@ public abstract class Patch extends OperationCriticalTransaction {
|
||||
if (dbType == DBType.MYSQL) enableForeignKeyChecks();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void apply() {
|
||||
db.executeTransaction(this);
|
||||
}
|
||||
|
||||
private void enableForeignKeyChecks() {
|
||||
execute("SET FOREIGN_KEY_CHECKS=1");
|
||||
}
|
||||
@ -101,7 +85,7 @@ public abstract class Patch extends OperationCriticalTransaction {
|
||||
}
|
||||
|
||||
protected void dropTable(String name) {
|
||||
execute(TableSqlParser.dropTable(name));
|
||||
execute("DROP TABLE " + name);
|
||||
}
|
||||
|
||||
protected void renameTable(String from, String to) {
|
||||
@ -143,8 +127,4 @@ public abstract class Patch extends OperationCriticalTransaction {
|
||||
|
||||
Verify.isTrue(constraints.isEmpty(), () -> new DBOpException("Table '" + table + "' has constraints '" + constraints + "'"));
|
||||
}
|
||||
|
||||
protected UUID getServerUUID() {
|
||||
return db.getServerUUIDSupplier().get();
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.PingTable;
|
||||
|
||||
public class PingOptimizationPatch extends Patch {
|
||||
@ -25,8 +24,7 @@ public class PingOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public PingOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public PingOptimizationPatch() {
|
||||
tableName = PingTable.TABLE_NAME;
|
||||
tempTableName = "temp_ping";
|
||||
}
|
||||
|
@ -16,15 +16,10 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.SessionsTable;
|
||||
|
||||
public class SessionAFKTimePatch extends Patch {
|
||||
|
||||
public SessionAFKTimePatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return hasColumn(SessionsTable.TABLE_NAME, SessionsTable.AFK_TIME);
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.SessionsTable;
|
||||
|
||||
public class SessionsOptimizationPatch extends Patch {
|
||||
@ -25,8 +24,7 @@ public class SessionsOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public SessionsOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public SessionsOptimizationPatch() {
|
||||
tableName = SessionsTable.TABLE_NAME;
|
||||
tempTableName = "temp_sessions";
|
||||
}
|
||||
|
@ -16,14 +16,8 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
|
||||
public class TransferTableRemovalPatch extends Patch {
|
||||
|
||||
public TransferTableRemovalPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return !hasTable("plan_transfer");
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.UserInfoTable;
|
||||
|
||||
public class UserInfoOptimizationPatch extends Patch {
|
||||
@ -25,8 +24,7 @@ public class UserInfoOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public UserInfoOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public UserInfoOptimizationPatch() {
|
||||
tableName = UserInfoTable.TABLE_NAME;
|
||||
tempTableName = "temp_user_info";
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
|
||||
import com.djrapitops.plan.db.sql.tables.*;
|
||||
import com.djrapitops.plan.system.info.server.Server;
|
||||
@ -29,10 +28,6 @@ public class Version10Patch extends Patch {
|
||||
|
||||
private Integer serverID;
|
||||
|
||||
public Version10Patch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return !hasTable("plan_gamemodetimes");
|
||||
@ -40,7 +35,7 @@ public class Version10Patch extends Patch {
|
||||
|
||||
@Override
|
||||
protected void applyPatch() {
|
||||
Optional<Server> server = db.query(ServerQueries.fetchServerMatchingIdentifier(getServerUUID()));
|
||||
Optional<Server> server = query(ServerQueries.fetchServerMatchingIdentifier(getServerUUID()));
|
||||
serverID = server.map(Server::getId)
|
||||
.orElseThrow(() -> new IllegalStateException("Server UUID was not registered, try rebooting the plugin."));
|
||||
|
||||
|
@ -16,14 +16,8 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
|
||||
public class VersionTableRemovalPatch extends Patch {
|
||||
|
||||
public VersionTableRemovalPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return !hasTable("plan_version");
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.WorldTimesTable;
|
||||
|
||||
public class WorldTimesOptimizationPatch extends Patch {
|
||||
@ -25,8 +24,7 @@ public class WorldTimesOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public WorldTimesOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public WorldTimesOptimizationPatch() {
|
||||
tableName = WorldTimesTable.TABLE_NAME;
|
||||
tempTableName = "temp_world_times";
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.ExecBatchStatement;
|
||||
import com.djrapitops.plan.db.access.QueryStatement;
|
||||
import com.djrapitops.plan.db.access.queries.schema.SessionIDServerIDRelationQuery;
|
||||
@ -29,10 +28,6 @@ import java.util.Map;
|
||||
|
||||
public class WorldTimesSeverIDPatch extends Patch {
|
||||
|
||||
public WorldTimesSeverIDPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
String tableName = WorldTimesTable.TABLE_NAME;
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBOpException;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.sql.tables.WorldTable;
|
||||
|
||||
public class WorldsOptimizationPatch extends Patch {
|
||||
@ -25,8 +24,7 @@ public class WorldsOptimizationPatch extends Patch {
|
||||
private String tempTableName;
|
||||
private String tableName;
|
||||
|
||||
public WorldsOptimizationPatch(SQLDB db) {
|
||||
super(db);
|
||||
public WorldsOptimizationPatch() {
|
||||
tableName = WorldTable.TABLE_NAME;
|
||||
tempTableName = "temp_worlds";
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.patches;
|
||||
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.ExecBatchStatement;
|
||||
import com.djrapitops.plan.db.access.QueryAllStatement;
|
||||
import com.djrapitops.plan.db.access.QueryStatement;
|
||||
@ -37,10 +36,6 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*;
|
||||
|
||||
public class WorldsServerIDPatch extends Patch {
|
||||
|
||||
public WorldsServerIDPatch(SQLDB db) {
|
||||
super(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
String tableName = WorldTable.TABLE_NAME;
|
||||
@ -69,7 +64,7 @@ public class WorldsServerIDPatch extends Patch {
|
||||
|
||||
@Override
|
||||
protected void applyPatch() {
|
||||
Collection<UUID> serverUUIDs = db.query(ServerQueries.fetchPlanServerInformation()).keySet();
|
||||
Collection<UUID> serverUUIDs = query(ServerQueries.fetchPlanServerInformation()).keySet();
|
||||
|
||||
Map<UUID, Collection<String>> worldsPerServer = new HashMap<>();
|
||||
for (UUID serverUUID : serverUUIDs) {
|
||||
|
@ -80,14 +80,19 @@ public class ServerServerInfo extends ServerInfo {
|
||||
try {
|
||||
return serverUUID.isPresent() ? updateDbInfo(serverUUID.get()) : registerServer();
|
||||
} catch (DBOpException e) {
|
||||
String causeMsg = e.getCause().getMessage();
|
||||
String causeMsg = e.getMessage();
|
||||
throw new EnableException("Failed to read Server information from Database: " + causeMsg, e);
|
||||
} catch (IOException e) {
|
||||
throw new EnableException("Failed to read ServerInfoFile.yml", e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null; // This line is not reached due to the thread interrupt.
|
||||
} catch (Exception e) {
|
||||
throw new EnableException("Failed to perform a database transaction to store the server information", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Server updateDbInfo(UUID serverUUID) throws IOException {
|
||||
private Server updateDbInfo(UUID serverUUID) throws Exception {
|
||||
Database db = dbSystem.getDatabase();
|
||||
|
||||
Optional<Server> foundServer = db.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID));
|
||||
@ -111,11 +116,11 @@ public class ServerServerInfo extends ServerInfo {
|
||||
return server;
|
||||
}
|
||||
|
||||
private Server registerServer() throws IOException {
|
||||
private Server registerServer() throws Exception {
|
||||
return registerServer(generateNewUUID());
|
||||
}
|
||||
|
||||
private Server registerServer(UUID serverUUID) throws IOException {
|
||||
private Server registerServer(UUID serverUUID) throws Exception {
|
||||
Database db = dbSystem.getDatabase();
|
||||
|
||||
// Create the server object
|
||||
@ -125,7 +130,8 @@ public class ServerServerInfo extends ServerInfo {
|
||||
Server server = new Server(-1, serverUUID, name, webAddress, maxPlayers);
|
||||
|
||||
// Save
|
||||
db.executeTransaction(new StoreServerInformationTransaction(server));
|
||||
db.executeTransaction(new StoreServerInformationTransaction(server))
|
||||
.get(); // Wait until transaction has completed
|
||||
|
||||
// Load from database
|
||||
server = db.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID))
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.utilities.java;
|
||||
|
||||
import com.djrapitops.plugin.utilities.ArrayUtil;
|
||||
|
||||
/**
|
||||
* Utilities for manipulating different Throwables.
|
||||
*
|
||||
* @author Rsl1122
|
||||
*/
|
||||
public class ThrowableUtils {
|
||||
|
||||
private ThrowableUtils() {
|
||||
/* Static method class */
|
||||
}
|
||||
|
||||
public static void appendEntryPointToCause(Throwable throwable, Throwable originPoint) {
|
||||
Throwable cause = throwable.getCause();
|
||||
while (cause.getCause() != null) {
|
||||
cause = cause.getCause();
|
||||
}
|
||||
cause.setStackTrace(ArrayUtil.merge(cause.getStackTrace(), originPoint.getStackTrace()));
|
||||
}
|
||||
|
||||
public static String findCallerAfterClass(StackTraceElement[] stackTrace, Class afterThis) {
|
||||
boolean found = false;
|
||||
for (StackTraceElement stackTraceElement : stackTrace) {
|
||||
if (found) {
|
||||
return stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName();
|
||||
}
|
||||
if (stackTraceElement.getClassName().contains(afterThis.getName())) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
}
|
@ -55,6 +55,7 @@ import com.djrapitops.plan.system.settings.paths.WebserverSettings;
|
||||
import com.djrapitops.plan.utilities.SHA256Hash;
|
||||
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
|
||||
import com.djrapitops.plugin.logging.console.TestPluginLogger;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import org.junit.*;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.rules.Timeout;
|
||||
@ -108,7 +109,7 @@ public abstract class CommonDBTest {
|
||||
|
||||
dbSystem = system.getDatabaseSystem();
|
||||
db = (SQLDB) dbSystem.getActiveDatabaseByName(dbName);
|
||||
|
||||
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
db.init();
|
||||
|
||||
serverUUID = system.getServerInfo().getServerUUID();
|
||||
@ -120,8 +121,8 @@ public abstract class CommonDBTest {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws DBInitException {
|
||||
new Patch(db) {
|
||||
public void setUp() {
|
||||
db.executeTransaction(new Patch() {
|
||||
@Override
|
||||
public boolean hasBeenApplied() {
|
||||
return false;
|
||||
@ -135,7 +136,7 @@ public abstract class CommonDBTest {
|
||||
dropTable("plan_worlds");
|
||||
dropTable("plan_users");
|
||||
}
|
||||
}.apply();
|
||||
});
|
||||
db.executeTransaction(new CreateTablesTransaction());
|
||||
db.executeTransaction(new RemoveEverythingTransaction());
|
||||
|
||||
@ -157,13 +158,6 @@ public abstract class CommonDBTest {
|
||||
db.init();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoExceptionWhenCommitEmpty() throws Exception {
|
||||
db.commit(db.getConnection());
|
||||
db.commit(db.getConnection());
|
||||
db.commit(db.getConnection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveCommandUse() throws DBInitException {
|
||||
Map<String, Integer> expected = new HashMap<>();
|
||||
@ -243,19 +237,20 @@ public abstract class CommonDBTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void geoInformationIsStored() throws DBInitException {
|
||||
public void geoInformationIsStored() throws DBInitException, NoSuchAlgorithmException {
|
||||
saveUserOne();
|
||||
|
||||
String expectedIP = "1.2.3.4";
|
||||
String expectedGeoLoc = "TestLocation";
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
GeoInfo expected = new GeoInfo(expectedIP, expectedGeoLoc, time, "3");
|
||||
saveGeoInfo(playerUUID, expected);
|
||||
saveGeoInfo(playerUUID, new GeoInfo(expectedIP, expectedGeoLoc, time, "3"));
|
||||
commitTest();
|
||||
|
||||
List<GeoInfo> geolocations = db.query(GeoInfoQueries.fetchAllGeoInformation()).getOrDefault(playerUUID, new ArrayList<>());
|
||||
assertEquals(1, geolocations.size());
|
||||
|
||||
GeoInfo expected = new GeoInfo("1.2.xx.xx", expectedGeoLoc, time, new SHA256Hash(expectedIP).create());
|
||||
assertEquals(expected, geolocations.get(0));
|
||||
}
|
||||
|
||||
@ -651,6 +646,7 @@ public abstract class CommonDBTest {
|
||||
@Test
|
||||
public void testBackupAndRestore() throws Exception {
|
||||
H2DB backup = dbSystem.getH2Factory().usingFile(temporaryFolder.newFile("backup.db"));
|
||||
backup.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
backup.init();
|
||||
|
||||
saveAllData();
|
||||
|
@ -26,6 +26,7 @@ import com.djrapitops.plan.db.access.transactions.init.CreateTablesTransaction;
|
||||
import com.djrapitops.plan.db.patches.Patch;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@ -35,8 +36,6 @@ import rules.PluginComponentMocker;
|
||||
import utilities.OptionalAssert;
|
||||
import utilities.TestConstants;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Test for the patching of Plan 4.5.2 H2 DB into the newest schema.
|
||||
*
|
||||
@ -67,7 +66,7 @@ public class DBPatchH2RegressionTest extends DBPatchRegressionTest {
|
||||
private H2DB underTest;
|
||||
|
||||
@Before
|
||||
public void setUpDBWithOldSchema() throws DBInitException, SQLException {
|
||||
public void setUpDBWithOldSchema() throws DBInitException {
|
||||
PlanConfig config = component.getPlanSystem().getConfigSystem().getConfig();
|
||||
|
||||
config.set(DatabaseSettings.MYSQL_USER, "user");
|
||||
@ -75,11 +74,11 @@ public class DBPatchH2RegressionTest extends DBPatchRegressionTest {
|
||||
|
||||
underTest = component.getPlanSystem().getDatabaseSystem().getH2Factory()
|
||||
.usingFileCalled("test");
|
||||
|
||||
underTest.setOpen(true);
|
||||
underTest.setupDataSource();
|
||||
underTest.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
underTest.init();
|
||||
|
||||
// Initialize database with the old table schema
|
||||
dropAllTables(underTest);
|
||||
underTest.executeTransaction(new Transaction() {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
@ -112,11 +111,12 @@ public class DBPatchH2RegressionTest extends DBPatchRegressionTest {
|
||||
|
||||
@Test
|
||||
public void h2PatchTaskWorksWithoutErrors() {
|
||||
for (Patch patch : underTest.patches()) {
|
||||
Patch[] patches = underTest.patches();
|
||||
for (Patch patch : patches) {
|
||||
underTest.executeTransaction(patch);
|
||||
}
|
||||
|
||||
assertPatchesHaveBeenApplied(underTest);
|
||||
assertPatchesHaveBeenApplied(patches);
|
||||
|
||||
// Make sure that a fetch works.
|
||||
ServerContainer server = underTest.query(ContainerFetchQueries.fetchServerContainer(TestConstants.SERVER_UUID));
|
||||
|
@ -29,6 +29,7 @@ import com.djrapitops.plan.system.PlanSystem;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
|
||||
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import org.junit.*;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import rules.PluginComponentMocker;
|
||||
@ -90,11 +91,10 @@ public class DBPatchMySQLRegressionTest extends DBPatchRegressionTest {
|
||||
system.enable();
|
||||
|
||||
underTest = (MySQLDB) system.getDatabaseSystem().getActiveDatabaseByName(DBType.MYSQL.getName());
|
||||
underTest.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
underTest.init();
|
||||
|
||||
underTest.setOpen(true);
|
||||
underTest.setupDataSource();
|
||||
|
||||
dropAllTables();
|
||||
dropAllTables(underTest);
|
||||
|
||||
// Initialize database with the old table schema
|
||||
underTest.executeTransaction(new Transaction() {
|
||||
@ -122,17 +122,6 @@ public class DBPatchMySQLRegressionTest extends DBPatchRegressionTest {
|
||||
insertData(underTest);
|
||||
}
|
||||
|
||||
private void dropAllTables() {
|
||||
underTest.executeTransaction(new Transaction() {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
execute("DROP DATABASE Plan");
|
||||
execute("CREATE DATABASE Plan");
|
||||
execute("USE Plan");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@After
|
||||
public void closeDatabase() {
|
||||
underTest.close();
|
||||
@ -140,11 +129,12 @@ public class DBPatchMySQLRegressionTest extends DBPatchRegressionTest {
|
||||
|
||||
@Test
|
||||
public void mysqlPatchTaskWorksWithoutErrors() {
|
||||
for (Patch patch : underTest.patches()) {
|
||||
Patch[] patches = underTest.patches();
|
||||
for (Patch patch : patches) {
|
||||
underTest.executeTransaction(patch);
|
||||
}
|
||||
|
||||
assertPatchesHaveBeenApplied(underTest);
|
||||
assertPatchesHaveBeenApplied(patches);
|
||||
|
||||
// Make sure that a fetch works.
|
||||
ServerContainer server = underTest.query(ContainerFetchQueries.fetchServerContainer(TestConstants.SERVER_UUID));
|
||||
|
@ -16,7 +16,9 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db;
|
||||
|
||||
import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
import com.djrapitops.plan.db.patches.Patch;
|
||||
import com.djrapitops.plan.db.sql.tables.*;
|
||||
import utilities.TestConstants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -37,23 +39,49 @@ public abstract class DBPatchRegressionTest {
|
||||
String insertWorld = "INSERT INTO plan_worlds (server_id, world_name) VALUES (1, 'World')";
|
||||
String insertWorldTimes = "INSERT INTO plan_world_times (user_id, server_id, world_id, session_id, survival_time) VALUES (1,1,1,1,1234)";
|
||||
|
||||
|
||||
protected void insertData(SQLDB underTest) {
|
||||
underTest.execute(insertServer);
|
||||
underTest.execute(insertUser);
|
||||
underTest.execute(insertUser2);
|
||||
underTest.execute(insertUserInfo);
|
||||
underTest.execute(insertIP);
|
||||
underTest.execute(insertNickname);
|
||||
underTest.execute(insertSession);
|
||||
underTest.execute(insertKill);
|
||||
underTest.execute(insertWorld);
|
||||
underTest.execute(insertWorldTimes);
|
||||
protected void dropAllTables(SQLDB underTest) {
|
||||
underTest.executeTransaction(new Transaction() {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
execute("DROP TABLE " + CommandUseTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + GeoInfoTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + KillsTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + NicknamesTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + PingTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + SecurityTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + ServerTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + SessionsTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + SettingsTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + TPSTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + UserInfoTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + UsersTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + WorldTable.TABLE_NAME);
|
||||
execute("DROP TABLE " + WorldTimesTable.TABLE_NAME);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void assertPatchesHaveBeenApplied(SQLDB underTest) {
|
||||
protected void insertData(SQLDB underTest) {
|
||||
underTest.executeTransaction(new Transaction() {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
execute(insertServer);
|
||||
execute(insertUser);
|
||||
execute(insertUser2);
|
||||
execute(insertUserInfo);
|
||||
execute(insertIP);
|
||||
execute(insertNickname);
|
||||
execute(insertSession);
|
||||
execute(insertKill);
|
||||
execute(insertWorld);
|
||||
execute(insertWorldTimes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void assertPatchesHaveBeenApplied(Patch[] patches) {
|
||||
List<String> failed = new ArrayList<>();
|
||||
for (Patch patch : underTest.patches()) {
|
||||
for (Patch patch : patches) {
|
||||
if (!patch.hasBeenApplied()) {
|
||||
failed.add(patch.getClass().getSimpleName());
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
import com.djrapitops.plan.db.access.transactions.commands.RemoveEverythingTransaction;
|
||||
import com.djrapitops.plan.db.access.transactions.init.CreateTablesTransaction;
|
||||
import com.djrapitops.plan.db.patches.Patch;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@ -33,8 +34,6 @@ import rules.PluginComponentMocker;
|
||||
import utilities.OptionalAssert;
|
||||
import utilities.TestConstants;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Test for the patching of Plan 4.5.2 SQLite DB into the newest schema.
|
||||
*
|
||||
@ -65,14 +64,14 @@ public class DBPatchSQLiteRegressionTest extends DBPatchRegressionTest {
|
||||
private SQLiteDB underTest;
|
||||
|
||||
@Before
|
||||
public void setUpDBWithOldSchema() throws DBInitException, SQLException {
|
||||
public void setUpDBWithOldSchema() throws DBInitException {
|
||||
underTest = component.getPlanSystem().getDatabaseSystem().getSqLiteFactory()
|
||||
.usingFileCalled("test");
|
||||
|
||||
underTest.setOpen(true);
|
||||
underTest.setupDataSource();
|
||||
underTest.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
underTest.init();
|
||||
|
||||
// Initialize database with the old table schema
|
||||
dropAllTables(underTest);
|
||||
underTest.executeTransaction(new Transaction() {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
@ -105,11 +104,12 @@ public class DBPatchSQLiteRegressionTest extends DBPatchRegressionTest {
|
||||
|
||||
@Test
|
||||
public void sqlitePatchTaskWorksWithoutErrors() {
|
||||
for (Patch patch : underTest.patches()) {
|
||||
Patch[] patches = underTest.patches();
|
||||
for (Patch patch : patches) {
|
||||
underTest.executeTransaction(patch);
|
||||
}
|
||||
|
||||
assertPatchesHaveBeenApplied(underTest);
|
||||
assertPatchesHaveBeenApplied(patches);
|
||||
|
||||
// Make sure that a fetch works.
|
||||
ServerContainer server = underTest.query(ContainerFetchQueries.fetchServerContainer(TestConstants.SERVER_UUID));
|
||||
|
@ -18,6 +18,7 @@ package com.djrapitops.plan.db;
|
||||
|
||||
import com.djrapitops.plan.data.container.GeoInfo;
|
||||
import com.djrapitops.plan.db.access.queries.ServerAggregateQueries;
|
||||
import com.djrapitops.plan.db.access.transactions.Transaction;
|
||||
import com.djrapitops.plan.db.access.transactions.events.PlayerRegisterTransaction;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
|
||||
@ -59,9 +60,14 @@ public class MySQLTest extends CommonDBTest {
|
||||
}
|
||||
|
||||
private static void clearDatabase() {
|
||||
db.execute("DROP DATABASE Plan");
|
||||
db.execute("CREATE DATABASE Plan");
|
||||
db.execute("USE Plan");
|
||||
db.executeTransaction(new Transaction() {
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
execute("DROP DATABASE Plan");
|
||||
execute("CREATE DATABASE Plan");
|
||||
execute("USE Plan");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -31,6 +31,7 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Manages Server information on the Bungee instance.
|
||||
@ -70,8 +71,10 @@ public class VelocityServerInfo extends ServerInfo {
|
||||
} else {
|
||||
server = registerVelocityInfo(database);
|
||||
}
|
||||
} catch (DBOpException e) {
|
||||
} catch (DBOpException | ExecutionException e) {
|
||||
throw new EnableException("Failed to read Server information from Database.");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return server;
|
||||
}
|
||||
@ -93,13 +96,14 @@ public class VelocityServerInfo extends ServerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
private Server registerVelocityInfo(Database db) throws EnableException {
|
||||
private Server registerVelocityInfo(Database db) throws EnableException, ExecutionException, InterruptedException {
|
||||
UUID serverUUID = generateNewUUID();
|
||||
String accessAddress = webServer.get().getAccessAddress();
|
||||
|
||||
// TODO Rework to allow Velocity as name.
|
||||
Server proxy = new Server(-1, serverUUID, "BungeeCord", accessAddress, serverProperties.getMaxPlayers());
|
||||
db.executeTransaction(new StoreServerInformationTransaction(proxy));
|
||||
db.executeTransaction(new StoreServerInformationTransaction(proxy))
|
||||
.get();
|
||||
|
||||
Optional<Server> proxyInfo = db.query(ServerQueries.fetchProxyServerInformation());
|
||||
if (proxyInfo.isPresent()) {
|
||||
|
@ -16,11 +16,13 @@
|
||||
*/
|
||||
package com.djrapitops.plan;
|
||||
|
||||
import com.djrapitops.plan.db.SQLiteDB;
|
||||
import com.djrapitops.plan.system.PlanSystem;
|
||||
import com.djrapitops.plan.system.database.DBSystem;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.ProxySettings;
|
||||
import com.djrapitops.plan.system.settings.paths.WebserverSettings;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
@ -56,7 +58,9 @@ public class VelocitySystemTest {
|
||||
config.set(ProxySettings.IP, "8.8.8.8");
|
||||
|
||||
DBSystem dbSystem = velocitySystem.getDatabaseSystem();
|
||||
dbSystem.setActiveDatabase(dbSystem.getSqLiteFactory().usingDefaultFile());
|
||||
SQLiteDB db = dbSystem.getSqLiteFactory().usingDefaultFile();
|
||||
db.setTransactionExecutorServiceProvider(MoreExecutors::newDirectExecutorService);
|
||||
dbSystem.setActiveDatabase(db);
|
||||
|
||||
velocitySystem.enable();
|
||||
assertTrue(velocitySystem.isEnabled());
|
||||
|
Loading…
Reference in New Issue
Block a user