Database support
This commit is contained in:
@@ -4,13 +4,20 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import cz.marwland.mc.core.config.ConfigManager;
|
||||
import cz.marwland.mc.core.features.Feature;
|
||||
import cz.marwland.mc.core.features.ModuleClassLoader;
|
||||
import cz.marwland.mc.core.storage.SQLStorage;
|
||||
import cz.marwland.mc.core.storage.StorageCredentials;
|
||||
import cz.marwland.mc.core.storage.impl.MariaDbConnectionFactory;
|
||||
import cz.marwland.mc.core.storage.impl.MySqlConnectionFactory;
|
||||
import cz.marwland.mc.core.util.ConfigUtil;
|
||||
|
||||
public class MarwCore extends JavaPlugin {
|
||||
|
||||
@@ -19,6 +26,7 @@ public class MarwCore extends JavaPlugin {
|
||||
private static MarwCore INSTANCE = null;
|
||||
private File modulesFolder = null;
|
||||
private ModuleClassLoader moduleClassLoader;
|
||||
private SQLStorage sqlStorage;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
@@ -28,6 +36,7 @@ public class MarwCore extends JavaPlugin {
|
||||
configManager = new ConfigManager(this);
|
||||
configManager.registerConfig("config.yml");
|
||||
configManager.loadConfig("config.yml");
|
||||
this.loadDatabase();
|
||||
|
||||
modulesFolder = this.getModulesFolderPath().toFile();
|
||||
modulesFolder.mkdirs();
|
||||
@@ -38,6 +47,7 @@ public class MarwCore extends JavaPlugin {
|
||||
public void onDisable() {
|
||||
this.features.forEach((k, v) -> v.onDisable());
|
||||
this.configManager.save();
|
||||
this.sqlStorage.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,6 +58,7 @@ public class MarwCore extends JavaPlugin {
|
||||
public void reload() {
|
||||
this.reloadConfig();
|
||||
this.features.forEach((k, v) -> v.onDisable());
|
||||
this.loadDatabase();
|
||||
this.loadAndEnableModules();
|
||||
}
|
||||
|
||||
@@ -107,4 +118,43 @@ public class MarwCore extends JavaPlugin {
|
||||
return this.moduleClassLoader;
|
||||
}
|
||||
|
||||
public void loadDatabase() {
|
||||
if (this.sqlStorage != null)
|
||||
this.sqlStorage.shutdown();
|
||||
|
||||
FileConfiguration cfg = this.getConfigManager().getConfig("config.yml");
|
||||
|
||||
Map<String, String> props = ConfigUtil.getStringMap(cfg, "data.pool-settings.properties");
|
||||
if (props == null)
|
||||
props = new HashMap<>();
|
||||
|
||||
StorageCredentials creds = new StorageCredentials(
|
||||
cfg.getString("dataSource.address"),
|
||||
cfg.getString("dataSource.database"),
|
||||
cfg.getString("dataSource.username"),
|
||||
cfg.getString("dataSource.password"),
|
||||
cfg.getString("dataSource.table-prefix"),
|
||||
cfg.getInt("dataSource.pool-settings.maximum-pool-size"),
|
||||
cfg.getInt("dataSource.pool-settings.minimum-idle"),
|
||||
cfg.getInt("dataSource.pool-settings.maximum-lifetime"),
|
||||
cfg.getInt("dataSource.pool-settings.connection-timeout"),
|
||||
props
|
||||
);
|
||||
|
||||
String method = cfg.getString("method");
|
||||
if (method.equalsIgnoreCase("mariadb")) {
|
||||
this.sqlStorage = new SQLStorage(new MariaDbConnectionFactory(creds));
|
||||
} else if (method.equalsIgnoreCase("mysql")) {
|
||||
this.sqlStorage = new SQLStorage(new MySqlConnectionFactory(creds));
|
||||
} else {
|
||||
this.getLogger().log(Level.CONFIG, "Invalid dataSource.method in config.yml: " + method);
|
||||
return;
|
||||
}
|
||||
sqlStorage.init();
|
||||
}
|
||||
|
||||
public SQLStorage getStorage() {
|
||||
return this.sqlStorage;
|
||||
}
|
||||
|
||||
}
|
||||
|
18
src/cz/marwland/mc/core/storage/ConnectionFactory.java
Normal file
18
src/cz/marwland/mc/core/storage/ConnectionFactory.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package cz.marwland.mc.core.storage;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface ConnectionFactory {
|
||||
|
||||
void init();
|
||||
void shutdown();
|
||||
|
||||
Connection getConnection() throws SQLException;
|
||||
|
||||
Function<String, String> getStatementProcessor();
|
||||
|
||||
StorageCredentials getConfiguration();
|
||||
|
||||
}
|
84
src/cz/marwland/mc/core/storage/HikariConnectionFactory.java
Normal file
84
src/cz/marwland/mc/core/storage/HikariConnectionFactory.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package cz.marwland.mc.core.storage;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
public abstract class HikariConnectionFactory implements ConnectionFactory {
|
||||
|
||||
protected final StorageCredentials configuration;
|
||||
private HikariDataSource hikari;
|
||||
|
||||
public HikariConnectionFactory(StorageCredentials configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
protected String getDriverClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void appendProperties(HikariConfig config, StorageCredentials credentials) {
|
||||
for (Map.Entry<String, String> property : credentials.getProperties().entrySet()) {
|
||||
config.addDataSourceProperty(property.getKey(), property.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
protected void appendConfigurationInfo(HikariConfig config) {
|
||||
String address = this.configuration.getAddress();
|
||||
String[] addressSplit = address.split(":");
|
||||
address = addressSplit[0];
|
||||
String port = addressSplit.length > 1 ? addressSplit[1] : "3306";
|
||||
|
||||
config.setDataSourceClassName(getDriverClass());
|
||||
config.addDataSourceProperty("serverName", address);
|
||||
config.addDataSourceProperty("port", port);
|
||||
config.addDataSourceProperty("databaseName", this.configuration.getDatabase());
|
||||
config.setUsername(this.configuration.getUsername());
|
||||
config.setPassword(this.configuration.getPassword());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setPoolName("luckperms-hikari");
|
||||
|
||||
appendConfigurationInfo(config);
|
||||
appendProperties(config, this.configuration);
|
||||
|
||||
config.setMaximumPoolSize(this.configuration.getMaxPoolSize());
|
||||
config.setMinimumIdle(this.configuration.getMinIdleConnections());
|
||||
config.setMaxLifetime(this.configuration.getMaxLifetime());
|
||||
config.setConnectionTimeout(this.configuration.getConnectionTimeout());
|
||||
|
||||
// don't perform any initial connection validation - we subsequently call #getConnection
|
||||
// to setup the schema anyways
|
||||
config.setInitializationFailTimeout(-1);
|
||||
|
||||
this.hikari = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (this.hikari != null) {
|
||||
this.hikari.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
Connection connection = this.hikari.getConnection();
|
||||
if (connection == null) {
|
||||
throw new SQLException("Unable to get a connection from the pool.");
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageCredentials getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
}
|
40
src/cz/marwland/mc/core/storage/SQLStorage.java
Normal file
40
src/cz/marwland/mc/core/storage/SQLStorage.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package cz.marwland.mc.core.storage;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SQLStorage {
|
||||
|
||||
private final ConnectionFactory connectionFactory;
|
||||
private final StorageCredentials configuration;
|
||||
private final Function<String, String> statementProcessor;
|
||||
|
||||
public SQLStorage(ConnectionFactory connectionFactory) {
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.configuration = connectionFactory.getConfiguration();
|
||||
this.statementProcessor = connectionFactory.getStatementProcessor().compose(s -> s.replace("{prefix}", this.configuration.getTablePrefix()));
|
||||
}
|
||||
|
||||
public ConnectionFactory getConnectionFactory() {
|
||||
return this.connectionFactory;
|
||||
}
|
||||
|
||||
public StorageCredentials getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
public Function<String, String> getStatementProcessor() {
|
||||
return this.statementProcessor;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
this.connectionFactory.init();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
try {
|
||||
this.connectionFactory.shutdown();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
70
src/cz/marwland/mc/core/storage/StorageCredentials.java
Normal file
70
src/cz/marwland/mc/core/storage/StorageCredentials.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package cz.marwland.mc.core.storage;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class StorageCredentials {
|
||||
private final String address;
|
||||
private final String database;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final String tablePrefix;
|
||||
private final int maxPoolSize;
|
||||
private final int minIdleConnections;
|
||||
private final int maxLifetime;
|
||||
private final int connectionTimeout;
|
||||
private final Map<String, String> properties;
|
||||
|
||||
public StorageCredentials(String address, String database, String username, String password, String tablePrefix, int maxPoolSize, int minIdleConnections, int maxLifetime, int connectionTimeout, Map<String, String> properties) {
|
||||
this.address = address;
|
||||
this.database = database;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.tablePrefix = tablePrefix;
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
this.minIdleConnections = minIdleConnections;
|
||||
this.maxLifetime = maxLifetime;
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public String getDatabase() {
|
||||
return this.database;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public String getTablePrefix() {
|
||||
return this.tablePrefix;
|
||||
}
|
||||
|
||||
public int getMaxPoolSize() {
|
||||
return this.maxPoolSize;
|
||||
}
|
||||
|
||||
public int getMinIdleConnections() {
|
||||
return this.minIdleConnections;
|
||||
}
|
||||
|
||||
public int getMaxLifetime() {
|
||||
return this.maxLifetime;
|
||||
}
|
||||
|
||||
public int getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package cz.marwland.mc.core.storage.impl;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import cz.marwland.mc.core.storage.HikariConnectionFactory;
|
||||
import cz.marwland.mc.core.storage.StorageCredentials;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MariaDbConnectionFactory extends HikariConnectionFactory {
|
||||
|
||||
public MariaDbConnectionFactory(StorageCredentials configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDriverClass() {
|
||||
return "org.mariadb.jdbc.MariaDbDataSource";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(HikariConfig config, StorageCredentials credentials) {
|
||||
Set<Map.Entry<String, String>> properties = credentials.getProperties().entrySet();
|
||||
if (properties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String propertiesString = properties.stream().map(e -> e.getKey() + "=" + e.getValue())
|
||||
.collect(Collectors.joining(";"));
|
||||
|
||||
// kinda hacky. this will call #setProperties on the datasource, which will
|
||||
// append these options
|
||||
// onto the connections.
|
||||
config.addDataSourceProperty("properties", propertiesString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<String, String> getStatementProcessor() {
|
||||
return s -> s.replace("'", "`"); // use backticks for quotes
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package cz.marwland.mc.core.storage.impl;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import cz.marwland.mc.core.storage.HikariConnectionFactory;
|
||||
import cz.marwland.mc.core.storage.StorageCredentials;
|
||||
|
||||
public class MySqlConnectionFactory extends HikariConnectionFactory {
|
||||
|
||||
public MySqlConnectionFactory(StorageCredentials configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDriverClass() {
|
||||
return "com.mysql.jdbc.jdbc2.optional.MysqlDataSource";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(HikariConfig config, StorageCredentials credentials) {
|
||||
config.addDataSourceProperty("cachePrepStmts", "true");
|
||||
config.addDataSourceProperty("alwaysSendSetIsolation", "false");
|
||||
config.addDataSourceProperty("cacheServerConfiguration", "true");
|
||||
config.addDataSourceProperty("elideSetAutoCommits", "true");
|
||||
config.addDataSourceProperty("useLocalSessionState", "true");
|
||||
|
||||
config.addDataSourceProperty("useServerPrepStmts", "true");
|
||||
config.addDataSourceProperty("prepStmtCacheSize", "250");
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
|
||||
config.addDataSourceProperty("cacheCallableStmts", "true");
|
||||
|
||||
// append configurable properties
|
||||
super.appendProperties(config, credentials);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<String, String> getStatementProcessor() {
|
||||
return s -> s.replace("'", "`"); // use backticks for quotes
|
||||
}
|
||||
|
||||
}
|
25
src/cz/marwland/mc/core/util/ConfigUtil.java
Normal file
25
src/cz/marwland/mc/core/util/ConfigUtil.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package cz.marwland.mc.core.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
public class ConfigUtil {
|
||||
|
||||
public static Map<String, String> getStringMap(FileConfiguration cfg, String path) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
ConfigurationSection section = cfg.getConfigurationSection(path);
|
||||
|
||||
if (section == null)
|
||||
return null;
|
||||
|
||||
for (String key : section.getKeys(false)) {
|
||||
map.put(key, section.getString(key));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user